粘包一般出如今TCP的協議裏面,對於UDP來講是不會出現粘包情況的,之因此出現這種情況的緣由,涉及到一種名爲Nagle的算法。git
Nagle算法經過減小必須發送的封包的數量,提升網絡應用程序系統的效率,解決負載問題。通俗的講,就是在發包的時候會創建一個緩存區,發送的數據都會先進入這個緩存區,當上一條數據的接收被確認或者到達最大等待時間以後,纔會將緩存區的數據一塊發送過去,如此反覆。將小包進行整合,避免小包屢次發送形成的傳輸速度慢等問題。github
理論上來說,只要是基於TCP的socket連接,都須要處理粘包的狀況。算法
可能有些人在測試的時候並無出現粘包的狀況,認爲並不須要對粘包進行處理,這種想法是錯誤的。json
首先,在測試的時候之因此沒有出現粘包狀況,極有多是由於網絡路由問題,致使TCP的MTU會有所變化,Internet上的標準MTU值爲576,以太網MTU爲1500。因此測試粘包通常之外網環境進行測試。緩存
因爲Nagle算法已經成爲了默認的執行方式,因此對於粘包,在Server端和Client端都須要解決粘包的問題,因爲問題一致,因此解決方案也基本是通用的,只是相關語法須要變換一下。服務器
因爲iOS的底層對於socket的封裝並非那麼完善,使用的話還得采用C語言,所以使用了一個名爲CocoaAsyncSocket的第三方,將C的socket封裝成了適合OC面向對象的形式,github地址以下:https://github.com/robbiehanson/CocoaAsyncSocket網絡
導入成功後,正式開始處理粘包問題。app
首先,跟服務器肯定數據頭的問題,對於粘包,通常有兩種解決方案,第一種就是服務器返回的字段中有可識別的頭和尾,咱們能夠根據可識別的頭和尾來拆包。第二種是服務器返回的數據只包含頭,頭裏面有數據的長度,咱們能夠根據這個頭包含的數據長度來進行拆包。本文采用的即是第二種方案。socket
服務器返回的數據爲data形式的數據,打印出來的數據以下:測試
<fefd00f9 7b22736e 223a342c 22766572 73696f6e 223a2231 2e30222c 226e6574 466c6167 223a312c 22636d64 54797065 223a312c 22706475 223a7b22 70647554 79706522 3a343039 382c2264 65764461 7461223a 5b7b2264 65764964 223a2231 32333435 36373822 2c226465 764e616d 65223a22 e59b9ee8 b7afe68e a7e588b6 222c2270 4964223a 22343039 3833222c 22706172 616d223a 5b7b2274 79706522 3a383232 372c2276 616c7565 223a317d 2c7b2274 79706522 3a383232 382c2276 616c7565 223a307d 2c7b2274 79706522 3a383232 392c2276 616c7565 223a317d 2c7b2274 79706522 3a383233 302c2276 616c7565 223a317d 5d7d5d7d 7d>
其中fefd00f9爲服務器返回的頭,後面爲具體的數據。其中fefd佔兩個字節,00f9佔兩個字節,fefd是固定的值不作任何處理,00f9就是數據的長度
接下來,在連接成功以後,在回調方法裏面建立一個緩存區
- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port { self.readBuf = [[NSMutableData alloc] init];
NSLog(@"連接成功後的其餘操做");
}
而後在收到服務器發送的數據的時候,對這個數據進行處理
//服務器發送的數據 - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag { //將數據存入緩存區 [self.readBuf appendData:data]; //數據中前面有4個字節的頭信息,其中前兩位是固定的頭長度(用處不大),後兩位纔是數據的長度。 //若是大於4個字節證實有消息,由於服務器只要發送數據,一定包含頭 while (self.readBuf.length > 4) { //將消息轉化成byte,計算總長度 = 數據的內容長度 + 前面4個字節的頭長度 Byte *bytes = (Byte *)[self.readBuf bytes]; NSUInteger allLength = (bytes[2]<<8) + bytes[3] +4; //緩存區的長度大於總長度,證實有完整的數據包在緩存區,而後進行處理 if (self.readBuf.length >= allLength) { NSMutableData *msgData = [[self.readBuf subdataWithRange:NSMakeRange(0, allLength)] mutableCopy]; //提取出前面4個字節的頭內容,之因此提取出來,是由於在處理數據問題的時候,好比data轉json的時候,頭內容裏面包含非法字符,會致使轉化出來的json內容爲空,因此要先去掉再處理數據問題 [msgData replaceBytesInRange:NSMakeRange(0, 4) withBytes:NULL length:0]; NSLog(@"開始處理數據問題"); //處理完數據後將處理過的數據移出緩存區 _readBuf = [NSMutableData dataWithData:[_readBuf subdataWithRange:NSMakeRange(allLength, _readBuf.length - allLength)]]; }else{ //緩存區內數據包不是完整的,再次從服務器獲取數據,中斷while循環 [self.clientSocket readDataWithTimeout:-1 tag:0]; break; } } //讀取到服務端數據值後,能再次讀取 [self.clientSocket readDataWithTimeout:-1 tag:0]; }
這樣處理以後,能夠將粘包問題解決