鍍金池/ 問答/Java  C++  Linux/ socket 編程中的粘包問題

socket 編程中的粘包問題

關(guān)于粘包,查了一些資料,還是不太清楚。

我的理解是,A 與 B 的連接建立后,socket 將 tcp 字節(jié)流中的非數(shù)據(jù)字段進(jìn)行拆解,并將數(shù)據(jù)部分放入該連接的輸入緩沖區(qū)。當(dāng)有多個(gè) A->B 的包發(fā)來、且 B 沒有對(duì)每一條數(shù)據(jù)及時(shí)進(jìn)行處理時(shí),多條信息就會(huì)同時(shí)存在于輸入緩沖區(qū)中,首尾相連。如果數(shù)據(jù)部分沒有分隔符或能夠標(biāo)識(shí)數(shù)據(jù)長(zhǎng)度的字段,則很有可能出現(xiàn)無法分割出單條數(shù)據(jù)邊界的情況,從而導(dǎo)致「粘包」。

如果確實(shí)會(huì)出現(xiàn)「粘包」現(xiàn)象,則解決方法是不是應(yīng)該是:

  1. 確保接收方會(huì)及時(shí)處理發(fā)送來的數(shù)據(jù),使輸入緩沖區(qū)中同時(shí)只會(huì)存在一條發(fā)送來的數(shù)據(jù);
  2. 保證發(fā)送端的數(shù)據(jù)中有分隔符或能夠標(biāo)識(shí)其長(zhǎng)度的字段( 如 HTTP 消息 )。

不知道以上的理解是不是有誤?

回答
編輯回答
雨蝶

理解的沒啥問題,解決方案建議不要依賴于發(fā)送的數(shù)據(jù)一定能夠被立即處理,而是從下面的角度出發(fā):

  1. 比如只發(fā)送定包長(zhǎng)數(shù)據(jù),接收端判斷長(zhǎng)度即可?;蛘咴诎^中加入當(dāng)前包的長(zhǎng)度信息
  2. 添加邊際分割字符串(但是要注意文本中本身可能包含的可能性)
2017年9月30日 17:46
編輯回答
囍槑

首先,需要明確什么是tcp中的粘包問題(具體可wiki),我這里大致描述下:
tcp粘包就是指在tcp的網(wǎng)絡(luò)編程(當(dāng)然不止tcp,其他粘包也差不多意思)中,由于tcp是流式的,tcp提供給應(yīng)用層的接口,無法獲知發(fā)送端一次發(fā)了多少數(shù)據(jù),也沒辦法獲知發(fā)送端發(fā)了多少數(shù)據(jù),就造成下一次數(shù)據(jù)頭緊跟著上上一次數(shù)據(jù)尾,直接提供給應(yīng)用層,應(yīng)用層無法感知何處是發(fā)送端一次發(fā)送的邊界。

下面說一下tcp粘包具體的原因:是發(fā)送端tcp層可能會(huì)對(duì)數(shù)據(jù)封裝成多個(gè)tcp段,不僅如此,還會(huì)對(duì)應(yīng)用層的多次數(shù)據(jù)組成一個(gè)包,比如應(yīng)用層第一次寫'A',第二次寫'B',第三次寫'C',在Tcp層很可能是發(fā)送一個(gè)tcp段,包含的數(shù)據(jù)是'ABC',所以,這樣不管是接收端是否一次讀一個(gè)tcp段,都無法分辨出哪里是發(fā)送端應(yīng)用層邊界,俗稱"粘包"。

好,下面就簡(jiǎn)單說一下樓主(主要的錯(cuò)誤也就1點(diǎn)):
圖片描述

如我上面所說,接收端以如何的方式處理都不是粘包的根本原因,因此,接收端一次讀一個(gè)tcp段也是會(huì)造成粘包現(xiàn)象,從而到處嚴(yán)重的錯(cuò)誤。

那么如何解決粘包:
被采納的答案的兩條建議都是正確的,不過第一條容易讓人引起誤會(huì),以為tcp可以一次讀出定長(zhǎng)的數(shù)據(jù)。
沒什么好辦法,就是應(yīng)用層自己設(shè)置協(xié)議解決粘包問題,最典型的就是采用協(xié)議頭部和數(shù)據(jù)的方式。注意的是,這里應(yīng)該用狀態(tài)機(jī)實(shí)現(xiàn)接收端的功能,因?yàn)槟悴荒艽_定發(fā)送端發(fā)了完整的頭部,你就一定能收到完整的頭部。

最簡(jiǎn)單的辦法就是設(shè)置8個(gè)字節(jié)的頭部,表示數(shù)據(jù)長(zhǎng)度,接著就是數(shù)據(jù)。
那么發(fā)送端先發(fā)送8個(gè)字節(jié)的一個(gè)數(shù)字,表示接下來的數(shù)據(jù)大小是多少,接著將數(shù)據(jù)全部發(fā)送過去。接收端一直接受數(shù)據(jù),一直等到接受滿了8個(gè)數(shù)據(jù),取出來,計(jì)算出大小(假設(shè)為n),接著繼續(xù)讀取n個(gè)字節(jié),就是完整的一個(gè)應(yīng)用層的包了。

2017年4月25日 01:24