前段時(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
或者留言也行