socket程式設計[oc](粘包、半包處理)
阿新 • • 發佈:2019-02-08
在做socket程式設計時,如果是做tcp連線,那就不可避免的會遇到粘包與半包的問題,粘包就是多組資料被一併接收了,粘在了一起,無法做劃分;半包就是有資料接收不完整,無法處理。要解決粘包、半包的問題,一般在設計資料(訊息)格式時會約定好一個欄位專門用於描述資料包的長度,這樣就使資料有了邊界,依靠這個邊界,就能把每組資料劃分出來,資料不完整時也能獲知資料的缺失。
(當然也可以把資料設計成定長資料,但這樣不夠靈活;或者用\n,\r這類字元作為資料劃分依據,但不直觀、不明確,同時也不靈活)
舉個栗子:
訊息=訊息頭+訊息體。訊息頭用於描述訊息本身的基本資訊,訊息體則為訊息的具體內容
如上圖所示,假如我們的一個訊息是這麼定義的
訊息頭 = msgId(4B)+version(2B)+len(4B),共佔用10位元組
訊息體 = len中描述的16位元組長
所以這條訊息的長度就是 26位元組
可以看到,要想知道一條完整資料的邊界,關鍵就是訊息頭中的len欄位
假如我們現在接收到的資料是這樣的:
這個情況下即包含了粘包,也出現了半包的情況,三個資料包粘在了一起,最後一個數據包沒有接收完全,出現了半包的情況,看看程式碼如何處理
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { while (_readBuf.length >= 10)//因為頭部固定10個位元組,資料長度至少要大於10個位元組,我們才能得到完整的訊息描述資訊 { NSData *head = [_readBuf subdataWithRange:NSMakeRange(0, 10)];//取得頭部資料 NSData *lengthData = [head subdataWithRange:NSMakeRange(6, 4)];//取得長度資料 NSInteger length = [[[NSString alloc] initWithData:lengthData encoding:NSUTF8StringEncoding] integerValue];//得出內容長度 NSInteger complateDataLength = length + 10;//算出一個包完整的長度(內容長度+頭長度) if (_readBuf.length >= complateDataLength)//如果快取中資料夠一個整包的長度 { NSData *data = [_readBuf subdataWithRange:NSMakeRange(0, complateDataLength)];//擷取一個包的長度(處理粘包) [self handleTcpResponseData:data];//處理包資料 //從快取中截掉處理完的資料,繼續迴圈 _readBuf = [NSMutableData dataWithData:[_readBuf subdataWithRange:NSMakeRange(complateDataLength, _readBuf.length - complateDataLength)]]; } else//如果快取中的資料長度不夠一個包的長度,則包不完整(處理半包,繼續讀取) { [_socket readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];//繼續讀取資料 return; } } //快取中資料都處理完了,繼續讀取新資料 [_socket readDataWithTimeout:-1 buffer:_readBuf bufferOffset:_readBuf.length tag:0];//繼續讀取資料 }
下一篇 socket程式設計[oc](邏輯資料的處理) 點選開啟連結