關(guān)于GCDAsyncSocket的一些個人理解

GCDAsyncSocket

GCDAsyncSocket 的讀寫都是異步的不會阻塞線程。

socket讀寫都是數(shù)據(jù)流,這個流一直存在,心跳不是必須的。如果不斷開這個流通道就一直存在,讀取和寫入都通過這個流通道。

tag不參與流數(shù)據(jù)的傳遞,只是為了本地管理方便,是GCDAsyncSocket內(nèi)部實現(xiàn)數(shù)據(jù)調(diào)度的一個東西,方便用戶給數(shù)據(jù)打標(biāo)記,方便讀取。

發(fā)送信息在底層都是會被拆分成很細(xì)小的數(shù)據(jù)包發(fā)出去的,因此接受到的數(shù)據(jù)可以不是一次性接受完的。

每次可能收到一部分?jǐn)?shù)據(jù)。讀取時我們可能指定條件觸發(fā)讀取數(shù)據(jù)的方法。比如讀取多長的數(shù)據(jù)就觸發(fā),又比如碰到回車斷行數(shù)據(jù)就觸發(fā)。

也可以收到數(shù)據(jù)就觸發(fā),然后處理這部分?jǐn)?shù)據(jù)。

連接socket不必多說,主動調(diào)用才可以。鏈接socket會觸發(fā)代理方法。

發(fā)送消息,會觸發(fā)寫消息的代理方法。每發(fā)送一個消息,就應(yīng)該有一個讀取消息。

讀消息有很多種,指定讀取的條件會根據(jù)條件觸發(fā)讀消息的代理。

可以這么理解。主動調(diào)用讀消息,它就一直在那里可能是在后臺一直讀,當(dāng)讀到指定的條件時,觸發(fā)代理方法。

如果處理完一次消息還要繼續(xù)讀取下次進(jìn)來的數(shù)據(jù),就要繼續(xù)主動調(diào)用讀數(shù)據(jù)的方法,會根據(jù)讀數(shù)據(jù)的條件觸發(fā)代理方法。

用到的代理方法大致如下:

//socket成功連接到才會服務(wù)器調(diào)用
  -(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port;
 //接受到新的socket連接才會調(diào)用
  - (void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket;
 //讀取數(shù)據(jù),有數(shù)據(jù)就會調(diào)用
  - (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
 //直到讀到這個長度的數(shù)據(jù),才會觸發(fā)代理
  - (void)readDataToLength:(NSUInteger)length withTimeout:(NSTimeInterval)timeout tag:(long)tag; 
 //直到讀到data這個邊界,才會觸發(fā)代理 
  - (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
 //有socket斷開連接調(diào)用
  - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err;

發(fā)消息時,通常我們有確保我們沒有和服務(wù)器斷開,如果斷開了根據(jù)需要重連。

客戶端要維持自己的心跳,來讓服務(wù)器知道我們還連著。所謂心跳包就是開一個計時器,定時發(fā)送指定的信息。

服務(wù)器判斷 時間間隔,如果超過一定的時間,服務(wù)器沒收到來自客戶端的信息,服務(wù)端就會端口鏈接。

我們在socket鏈接之后,就可以發(fā)送心跳包了。

[sock readDataToData:[GCDAsyncSocket CRLFData] withTimeout:-1 tag:0];是讀取到邊界的意思。當(dāng)讀取到邊界時會觸發(fā)代理方法。

封包處理:

數(shù)據(jù)包的封裝,是需要一定的格式的,這個要和服務(wù)端協(xié)商一致。封裝數(shù)據(jù)包時,要根據(jù)格式封裝。

比如這個,先封裝一個數(shù)據(jù)頭,指定實際發(fā)送的數(shù)據(jù)內(nèi)容的大小,自身的一些標(biāo)志信息。以及一些分界標(biāo)志等。后面是具體的數(shù)據(jù)。

像這里的數(shù)據(jù)報頭,先轉(zhuǎn)成json 再轉(zhuǎn)成data 而不是直接轉(zhuǎn)data發(fā)出去的。因為服務(wù)端要對報頭進(jìn)行json解析。

當(dāng)然也可能是其他數(shù)據(jù)格式,比如protobuf消息數(shù)據(jù),對象數(shù)據(jù)等。

/*封裝報文**/
- (void)sendData:(NSData *)data :(NSString *)type toClinet:(NSString *)target;
{
    NSUInteger size = data.length;
    
    NSMutableDictionary *headDic = [NSMutableDictionary dictionary];
    [headDic setObject:type forKey:@"type"];
    [headDic setObject:@"CinentA" forKey:@"CinentID"];
    [headDic setObject:target forKey:@"targetID"];
    [headDic setObject:[NSString stringWithFormat:@"%ld",size] forKey:@"size"];
    NSString *jsonStr = [self dictionaryToJson:headDic];
    NSData *lengthData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
    NSMutableData *mData = [NSMutableData dataWithData:lengthData];
    //分界
    [mData appendData:[GCDAsyncSocket CRLFData]];
    
    [mData appendData:data];
    
    
    //第二個參數(shù),請求超時時間
    [self.clinetSocket writeData:mData withTimeout:-1 tag:0];
    
}
//字典轉(zhuǎn)為Json字符串
- (NSString *)dictionaryToJson:(NSDictionary *)dic
{
    NSError *error = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&error];
    return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

客戶端要做的主要是:

  • 鏈接服務(wù)器,并維持心跳 維持心跳不是必須的
  • 封包,給服務(wù)器發(fā)送消息
  • 接受處理服務(wù)器的信息

服務(wù)端要做的主要是:

  • 數(shù)據(jù)包的拆解,
  • 數(shù)據(jù)的分發(fā)
  • 用戶的調(diào)用
  • 每個用戶的心跳刷新 維持心跳不是必須的
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容