什么是粘包、拆包?
對於什么是粘包、拆包問題,我想先舉兩個簡單的應用場景:
- 客戶端和服務器建立一個連接,客戶端發送一條消息,客戶端關閉與服務端的連接。
- 客戶端和服務器簡歷一個連接,客戶端連續發送兩條消息,客戶端關閉與服務端的連接。
對於第一種情況,服務端的處理流程可以是這樣的:當客戶端與服務端的連接建立成功之后,服務端不斷讀取客戶端發送過來的數據,當客戶端與服務端連接斷開之后,服務端知道已經讀完了一條消息,然后進行解碼和后續處理...。對於第二種情況,如果按照上面相同的處理邏輯來處理,那就有問題了,我們來看看第二種情況下客戶端發送的兩條消息遞交到服務端有可能出現的情況:
第一種情況:
服務端一共讀到兩個數據包,第一個包包含客戶端發出的第一條消息的完整信息,第二個包包含客戶端發出的第二條消息,那這種情況比較好處理,服務器只需要簡單的從網絡緩沖區去讀就好了,第一次讀到第一條消息的完整信息,消費完再從網絡緩沖區將第二條完整消息讀出來消費。
沒有發生粘包、拆包示意圖
第二種情況:
服務端一共就讀到一個數據包,這個數據包包含客戶端發出的兩條消息的完整信息,這個時候基於之前邏輯實現的服務端就蒙了,因為服務端不知道第一條消息從哪兒結束和第二條消息從哪兒開始,這種情況其實是發生了TCP粘包。
TCP粘包示意圖
第三種情況:
服務端一共收到了兩個數據包,第一個數據包只包含了第一條消息的一部分,第一條消息的后半部分和第二條消息都在第二個數據包中,或者是第一個數據包包含了第一條消息的完整信息和第二條消息的一部分信息,第二個數據包包含了第二條消息的剩下部分,這種情況其實是發送了TCP拆,因為發生了一條消息被拆分在兩個包里面發送了,同樣上面的服務器邏輯對於這種情況是不好處理的。
TCP拆包示意圖
為什么會發生TCP粘包、拆包呢?
發生TCP粘包、拆包主要是由於下面一些原因:
- 應用程序寫入的數據大於套接字緩沖區大小,這將會發生拆包。
- 應用程序寫入數據小於套接字緩沖區大小,網卡將應用多次寫入的數據發送到網絡上,這將會發生粘包。
- 進行MSS(最大報文長度)大小的TCP分段,當TCP報文長度-TCP頭部長度>MSS的時候將發生拆包。
- 接收方法不及時讀取套接字緩沖區數據,這將發生粘包。
- ……
如何處理粘包、拆包問題?
知道了粘包、拆包問題及根源,那么如何處理粘包、拆包問題呢?TCP本身是面向流的,作為網絡服務器,如何從這源源不斷涌來的數據流中拆分出或者合並出有意義的信息呢?通常會有以下一些常用的方法:
- 使用帶消息頭的協議、消息頭存儲消息開始標識及消息長度信息,服務端獲取消息頭的時候解析出消息長度,然后向后讀取該長度的內容。
- 設置定長消息,服務端每次讀取既定長度的內容作為一條完整消息。
- 設置消息邊界,服務端從網絡流中按消息編輯分離出消息內容。
- ……
