Java网络编程基础之TCP粘包拆包
TCP是個“流”協(xié)議,所謂流,就是沒有界限的一串?dāng)?shù)據(jù)。大家可以想象河里的流水,他們是連成一片的,其間并沒有分界線。TCP底層并不了解上層業(yè)務(wù)數(shù)據(jù)的具體含義,他會根據(jù)TCP緩沖區(qū)的實際情況進(jìn)行包的劃分,所以在業(yè)務(wù)上認(rèn)為,一個完整的包可能會被TCP拆分成多個包進(jìn)行發(fā)送,也有可能把多個小的包封裝成一個大的數(shù)據(jù)包發(fā)送。這就是TCP所謂的拆包和粘包的問題。
一、TCP粘包/拆包問題說明
我們可以通過圖解對TCP粘包和拆包問題進(jìn)行說明,粘包問題如圖。
假設(shè)客戶端分別發(fā)送了兩個數(shù)據(jù)包D1和D2給服務(wù)端,由于服務(wù)端一次讀取到的字節(jié)數(shù)是不確定的,故可能存在以下4中情況。
服務(wù)端分兩次讀取到了兩個獨立的數(shù)據(jù)包,分別是D1和D2,沒有粘包和拆包。
服務(wù)端一次接收到了兩個數(shù)據(jù)包,D1和D2粘在一起,被稱為TCP粘包
服務(wù)端分兩次讀取到了兩個數(shù)據(jù)包,第一次讀取到了完整的D1包和D2包的部分內(nèi)容,第二次讀取到了D2包的剩余內(nèi)容,這被稱為TCP拆包。
服務(wù)端分兩次讀取到了兩個數(shù)據(jù)包,第一次讀取到了D1包的部分內(nèi)容D1_1,第二次讀取到了D1包的剩余內(nèi)容D1_2和D2包的整包。
如果此時服務(wù)端TCP接收滑窗非常小,而數(shù)據(jù)包D1和D2比較大,很有可能會發(fā)生第五種可能,即服務(wù)端分多次才能將D1和D2包接收完全,期間發(fā)生多次拆包。
二、TCP粘包/拆包發(fā)生的原因
問題產(chǎn)生的原因有三個,分別如下。
應(yīng)用程序write寫入的字節(jié)大小大于套接口發(fā)送緩沖區(qū)大小。
進(jìn)行MSS大小的TCP分段。
以太網(wǎng)幀的payload大于MTU進(jìn)行IP分片。
三、粘包問題的解決策略
由于底層的TCP無法理解上層的業(yè)務(wù)數(shù)據(jù),所以在底層是無法保證數(shù)據(jù)包不被拆分和重組的,這個問題只能通過上層的應(yīng)用協(xié)議棧設(shè)計來解決,根據(jù)業(yè)界的主流協(xié)議的解決方案,可以歸納如下。
消息定長,例如每個報文的大小為固定長度200字節(jié),如果不夠,空位補(bǔ)空格
在包尾增加回車換行符進(jìn)行分割,例如FTP協(xié)議
將消息分為消息頭和消息體,消息頭中包含表示消息總長度(或者消息體長度)的字段,通常涉及思路為消息頭的第一個字段使用int32來表示消息的總長度
更復(fù)雜的應(yīng)用層協(xié)議。
總結(jié)
以上是生活随笔為你收集整理的Java网络编程基础之TCP粘包拆包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 二叉树的遍历
- 下一篇: numpy.argmin 使用
