TCP 粘包拆包解決方案 By .net

粘包、拆包發(fā)生原因

由于TCP協(xié)議本身的機(jī)制(面向連接的可靠地協(xié)議-三次握手機(jī)制)客戶端與服務(wù)器會(huì)維持一個(gè)連接(Channel),數(shù)據(jù)在連接不斷開(kāi)的情況下,可以持續(xù)不斷地將數(shù)據(jù)包發(fā)往服務(wù)器。但是如果發(fā)送的網(wǎng)絡(luò)數(shù)據(jù)包太小,發(fā)送一次數(shù)據(jù)包的成本可能大大的超過(guò)了數(shù)據(jù)本身(4000%的消耗)這時(shí)候就需要啟用Nagle算法(可配置是否啟用)對(duì)較小的數(shù)據(jù)包進(jìn)行合并(基于此,TCP的網(wǎng)絡(luò)延遲要UDP的高些)然后再發(fā)送(超時(shí)或者包大小足夠)。那么這樣的話,服務(wù)器收到的數(shù)據(jù)就可能使多個(gè)數(shù)據(jù)流首位拼接在一起的, 服務(wù)器在接收到消息(數(shù)據(jù)流)的時(shí)候就無(wú)法將每個(gè)數(shù)據(jù)包區(qū)分開(kāi),這樣產(chǎn)生了粘包;再有服務(wù)器在接收到數(shù)據(jù)庫(kù)后,放到緩沖區(qū)中,如果消息沒(méi)有被及時(shí)從緩存區(qū)取走,下次在取數(shù)據(jù)的時(shí)候可能就會(huì)出現(xiàn)一次取出多個(gè)數(shù)據(jù)包的情況,也會(huì)造成粘包現(xiàn)象。

拆包產(chǎn)生的原因就簡(jiǎn)單的多:可能是IP分片傳輸導(dǎo)致的,也可能是傳輸過(guò)程中丟失部分包導(dǎo)致出現(xiàn)的半包,還有可能就是一個(gè)包可能被分成了兩次傳輸,在取數(shù)據(jù)的時(shí)候,先取到了一部分(還可能與接收的緩沖區(qū)大小有關(guān)系),總之就是一個(gè)數(shù)據(jù)包被分成了多次接收。

總結(jié)一下發(fā)生TCP粘包或拆包的原因,現(xiàn)列出常見(jiàn)的幾點(diǎn)

1.要發(fā)送的數(shù)據(jù)大于TCP發(fā)送緩沖區(qū)剩余空間大小,將會(huì)發(fā)生拆包。

2.待發(fā)送數(shù)據(jù)大于MSS(最大報(bào)文長(zhǎng)度),TCP在傳輸前將進(jìn)行拆包。

3.要發(fā)送的數(shù)據(jù)小于TCP發(fā)送緩沖區(qū)的大小,TCP將多次寫(xiě)入緩沖區(qū)的數(shù)據(jù)一次發(fā)送出去,將會(huì)發(fā)生粘包。

4.接收數(shù)據(jù)端的應(yīng)用層沒(méi)有及時(shí)讀取接收緩沖區(qū)中的數(shù)據(jù),將發(fā)生粘包。

尋找答案

無(wú)論時(shí)拆包還是粘包我們需要解決的首要問(wèn)題就是在設(shè)計(jì)上解決數(shù)據(jù)包的區(qū)分方法,在網(wǎng)上找到的比較好理解的方案

1.消息數(shù)據(jù)固定長(zhǎng)度,但是浪費(fèi)存儲(chǔ)和網(wǎng)絡(luò)資源

首先固定長(zhǎng)度是我首先想到可行的方法(簡(jiǎn)單、容易理解、也容易調(diào)試和排錯(cuò)),缺點(diǎn)也很明顯,長(zhǎng)度設(shè)計(jì)就很尷尬。過(guò)大很容易造成帶寬的浪費(fèi),太短了過(guò)長(zhǎng)的數(shù)據(jù)會(huì)被截?cái)唷?/p>

2.使用分割符來(lái)區(qū)分包的界限

分割符這個(gè)就……

3.數(shù)據(jù)包的頭部中增加數(shù)據(jù)包長(zhǎng)度字段

最大的問(wèn)題在于我們無(wú)法確定一個(gè)數(shù)據(jù)是否真的結(jié)束了。如果接收到的數(shù)據(jù)中斷了(數(shù)據(jù)頭部標(biāo)記長(zhǎng)度100實(shí)際只發(fā)送了50)然而我們并不知道,這將導(dǎo)致后面的數(shù)據(jù)全部無(wú)法正常讀取,我們沒(méi)有有辦法去修正這個(gè)錯(cuò)誤,甚至我們都無(wú)法察覺(jué)。

此外還有一種 RINGBUFFER 解決方案 , 這種方案本身過(guò)于復(fù)雜。因?yàn)槌杀驹颍ㄐ枰鄬?shí)踐和測(cè)試)放棄了

確定思路

固定長(zhǎng)度+包頭說(shuō)明

固定長(zhǎng)度能讓我們很好的避開(kāi)數(shù)據(jù)中斷了的問(wèn)題,因?yàn)楣潭ㄩL(zhǎng)度我們能很好的識(shí)別到數(shù)據(jù)的結(jié)尾。為每個(gè)數(shù)據(jù)包添加固定長(zhǎng)度的包頭用于描述是否是一個(gè)長(zhǎng)數(shù)據(jù)的一部分(包括位置)。這樣我們能很好的處理不同長(zhǎng)度的數(shù)據(jù),還能根據(jù)業(yè)務(wù)數(shù)據(jù)的實(shí)際情況調(diào)節(jié)單個(gè)數(shù)據(jù)包的長(zhǎng)度。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容