Java網絡編程之TCP粘包拆包
TCP是個“流”協議,所謂流,就是沒有界限的一串數據。大家可以想象河裏的流水,他們是連成一片的,其間並沒有分界線。TCP底層並不了解上層業務數據的具體含義,他會根據TCP緩衝區的實際情況進行包的劃分,所以在業務上認為,一個完整的包可能會被TCP拆分成多個包進行發送,也有可能把多個小的包封裝成一個大的數據包發送。這就是TCP所謂的拆包和粘包的問題。
一、TCP粘包/拆包問題說明
我們可以通過圖解對TCP粘包和拆包問題進行說明,粘包問題如圖。
假設客戶端分別發送了兩個數據包D1和D2給服務端,由於服務端一次讀取到的字節數是不確定的,故可能存在以下4中情況。
- 服務端分兩次讀取到了兩個獨立的數據包,分別是D1和D2,沒有粘包和拆包。
- 服務端一次接收到了兩個數據包,D1和D2粘在一起,被稱為TCP粘包
- 服務端分兩次讀取到了兩個數據包,第一次讀取到了完整的D1包和D2包的部分內容,第二次讀取到了D2包的剩餘內容,這被稱為TCP拆包。
- 服務端分兩次讀取到了兩個數據包,第一次讀取到了D1包的部分內容D1_1,第二次讀取到了D1包的剩餘內容D1_2和D2包的整包。
如果此時服務端TCP接收滑窗非常小,而數據包D1和D2比較大,很有可能會發生第五種可能,即服務端分多次才能將D1和D2包接收完全,期間發生多次拆包。
二、TCP粘包/拆包發生的原因
問題產生的原因有三個,分別如下。
- 應用程序write寫入的字節大小大於套接口發送緩衝區大小。
- 進行MSS大小的TCP分段。
- 以太網幀的payload大於MTU進行IP分片。
三、粘包問題的解決策略
由於底層的TCP無法理解上層的業務數據,所以在底層是無法保證數據包不被拆分和重組的,這個問題隻能通過上層的應用協議棧設計來解決,根據業界的主流協議的解決方案,可以歸納如下。
- 消息定長,例如每個報文的大小為固定長度200字節,如果不夠,空位補空格
- 在包尾增加回車換行符進行分割,例如FTP協議
- 將消息分為消息頭和消息體,消息頭中包含表示消息總長度(或者消息體長度)的字段,通常涉及思路為消息頭的第一個字段使用int32來表示消息的總長度
- 更複雜的應用層協議。
最後更新:2017-09-10 22:04:09