一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方式來處理資料,再加上網路上MTU的往往小於在應用處理的訊息資料,所以就會引發一次接收的資料無法滿足訊息的需要,導致粘包的存在。處理粘包的唯一方法就是制定應用層的資料通訊協議,通過協議來規範現有接收的資料是否滿足訊息資料的需要。在應用中處理粘包的基礎方法主要有兩種分別是以4節字描述訊息大小或以結束符,實際上也有兩者相結合的如HTTP,redis的通訊協議等。
在平時交流過程發現一些朋友即使做了這些協議的處理,但有時在處理資料的時候也會出現資料不對的情況。這主要原因他們在一些個別情況下沒有處理好。因為當一系列的訊息傳送過來的時候,對於4節字頭或結束符分佈位置都是不確定的。一種簡單的情況就是當前訊息處理完成後,緊接著就是處理一下個訊息的4節字描述,但在實際情況下當前接收的buffer剩下的內容有可能不足4節字的。如果你想通過通訊的程式來測這情況相對來說觸發的機率性不高,所以對於協議分析的功能最好通過單元測試來模擬。
通過下面這個圖可以更清晰地瞭解協議標記資料分佈的情況
下面簡單地介紹一下4位元組描述大小和結束符和處理方式。
4位元組大小描述方式
1 public void Import(byte[] data, int start, int count) 2 { 3 while (count > 0) 4 { 5 if (!mLoading) 6 { 7 mCheckSize.Reset(); 8 mStream.SetLength(0); 9 mStream.Position = 0; 10 mLoading = true; 11 } 12 if (mCheckSize.Length == -1) 13 { 14 while (count > 0 && mCheckSize.Length == -1) 15 { 16 mCheckSize.Import(data[start]); 17 start++; 18 count--; 19 } 20 } 21 else 22 { 23 if (OnImport(data, ref start, ref count)) 24 { 25 mLoading = false; 26 if (Receive != null) 27 { 28 mStream.Position = 0; 29 Receive(mStream); 30 } 31 } 32 } 33 } 34 } 35 36 37 public void Import(byte value) 38 { 39 LengthData[mIndex] = value; 40 if (mIndex == 3) 41 { 42 Length = BitConverter.ToInt32(LengthData, 0); 43 if (!LittleEndian) 44 Length = Endian.SwapInt32(Length); 45 } 46 else 47 { 48 mIndex++; 49 } 50 }
程式碼很簡單如果沒有長度描述的情況就把資料匯入到訊息長度描述的buffer中,如果當前buffer滿足4位的情況直接得到相應長度。後面的工作就是獲取相應長度的buffer即可。
結束符方式
1 public void Import(byte[] data, int start, int count) 2 { 3 while (count > 0) 4 { 5 if (!mLoading) 6 { 7 mStream.SetLength(0); 8 mStream.Position = 0; 9 mLoading = true; 10 } 11 if (data[x] == mEof[0]) 12 { 13 start += mEof.Length; 14 count -= mEof.Length; 15 mLoading = false; 16 if (Receive != null) 17 { 18 mStream.Position = 0; 19 Receive(mStream); 20 } 21 } 22 else 23 { 24 mStream.Write(data[start]); 25 start++; 26 count--; 27 } 28 } 29 }
結束符的處理方式就相對來說簡單多了。
以上就是兩種TCP資料處理粘包的情況,相關程式碼緊供參考。