關(guān)于Socket 粘包和半包的處理

前段時(shí)間在開發(fā)即時(shí)通訊,使用的是CocoaAsyncSocket 遇到了幾個(gè)常見的問題,網(wǎng)上說的關(guān)于粘包和半包其實(shí)就是數(shù)據(jù)的處理問題,TCP協(xié)議中,如果傳輸數(shù)據(jù)量比較大,會(huì)把數(shù)據(jù)拆分成流,這時(shí)就會(huì)出現(xiàn)數(shù)據(jù)不完整或者數(shù)據(jù)超出你想要的格式范圍,我們是一個(gè)聊天類型軟件,單發(fā)消息時(shí)因?yàn)閿?shù)據(jù)比較小,沒有出現(xiàn)問題,后面我寫了個(gè)循環(huán),循環(huán)50次發(fā)送一條消息,結(jié)果就出現(xiàn)后面的收不到的情況,就是這個(gè)粘包和半包導(dǎo)致的,后面查了相關(guān)資料,我先把我主要核心代碼附上

-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag{

while (self.readBuf.length>5) {
    NSData *head = [_readBuf subdataWithRange:NSMakeRange(0, 5)];//取得頭部數(shù)據(jù)
    NSData *lenthData = [head subdataWithRange:NSMakeRange(0, 2)];//取得長(zhǎng)度數(shù)據(jù)
    int length = [SocketUtils uint16FromBytes:lenthData];
    NSInteger lengthInteger = 0;
    lengthInteger = (NSInteger)length;//消息體應(yīng)該有l(wèi)engthInteger個(gè)字節(jié)長(zhǎng)度
    NSInteger complateDataLength = length + 5;//算出一個(gè)包完整的長(zhǎng)度(內(nèi)容長(zhǎng)度+頭長(zhǎng)度)
    
    
    if (self.readBuf.length >= complateDataLength)//如果緩存中數(shù)據(jù)夠一個(gè)整包的長(zhǎng)度
    {
        NSData *data = [_readBuf subdataWithRange:NSMakeRange(0, complateDataLength)];//截取一個(gè)包的長(zhǎng)度(處理粘包)
        [self configData:data];
        _readBuf = [NSMutableData dataWithData:[_readBuf subdataWithRange:NSMakeRange(complateDataLength, _readBuf.length - complateDataLength)]];
    }
    else//如果緩存中的數(shù)據(jù)長(zhǎng)度不夠一個(gè)包的長(zhǎng)度,則包不完整(處理半包,繼續(xù)讀取)
    {
        [gcdSocket readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];//繼續(xù)讀取數(shù)據(jù)
        return;
    }
    
}
[gcdSocket readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];//繼續(xù)讀取數(shù)據(jù)

}

上面這個(gè)方法是讀取數(shù)據(jù),處理數(shù)據(jù)的代理方法

然后我們使用的協(xié)議 包括消息頭和消息體

消息頭包含5個(gè)字節(jié),前面兩個(gè)字節(jié)說明消息體的長(zhǎng)度 是一個(gè)長(zhǎng)整型數(shù)據(jù) 中間兩個(gè)字節(jié)是命令碼,可以理解為接口名,最后一個(gè)字節(jié)是一個(gè)整形,告訴前端該數(shù)據(jù)是否被壓縮,然后結(jié)合上面的代碼和注釋就能很清楚的理解了

//解釋
self.readBuf 是我定義的一個(gè)全局的數(shù)據(jù)容器,用來緩存收到的半包數(shù)據(jù),如果該容器內(nèi)數(shù)據(jù)長(zhǎng)度大于5,說明已經(jīng)收到數(shù)據(jù)了,然后進(jìn)入while循環(huán),先截取數(shù)據(jù)長(zhǎng)度為5的消息頭,然后解析出來消息體的長(zhǎng)度 lengthInteger ,所以整個(gè)數(shù)據(jù)的長(zhǎng)度為 lengthInteger + 5,然后判斷self.readBuf的長(zhǎng)度, 如果大于 lengthInteger+5 說明有一個(gè)完整的數(shù)據(jù)包,此時(shí)就是粘包,就要去處理你的業(yè)務(wù)邏輯了,處理完這個(gè)包以后把self.readBuf里面的這個(gè)包內(nèi)容刪除,繼續(xù)處理剩下的內(nèi)容

如果長(zhǎng)度不夠 就說明是半包,就要繼續(xù)讀取數(shù)據(jù)

如果大家有哪里還搞不懂的可以私信我 WeChat :Arackboss

或者留言也行

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • http://zhaohuiopensource.iteye.com/blog/1541270 首先看兩個(gè)概念: ...
    52031d47791e閱讀 787評(píng)論 0 1
  • 在RPC框架中,粘包和拆包問題是必須解決一個(gè)問題,因?yàn)镽PC框架中,各個(gè)微服務(wù)相互之間都是維系了一個(gè)TCP長(zhǎng)連接,...
    AI喬治閱讀 3,066評(píng)論 0 7
  • 網(wǎng)絡(luò)編程 一.楔子 你現(xiàn)在已經(jīng)學(xué)會(huì)了寫python代碼,假如你寫了兩個(gè)python文件a.py和b.py,分別去運(yùn)...
    go以恒閱讀 2,246評(píng)論 0 6
  • 定義 TCP是個(gè)“流”協(xié)議,所謂流,就是沒有界限的一串?dāng)?shù)據(jù)。大家可以想象河里的流水,它們是連成一片的,其間并沒有分...
    tracy_668閱讀 616評(píng)論 0 1
  • 你是天上游逛的云, 我是地上莽撞的鹿。 因?yàn)轱L(fēng)行千里,吹動(dòng)了云的緣故, 我們?cè)谝豢瞄艠湎孪嘤觥?我在地上說云要?jiǎng)樱?..
    蘇格拉底有點(diǎn)困閱讀 994評(píng)論 2 4

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