首頁  >  文章  >  tcp黏包問題怎麼處理?

tcp黏包問題怎麼處理?

coldplay.xixi
coldplay.xixi原創
2020-06-28 13:02:557391瀏覽

tcp黏包問題處理的方法:1、定長發送法,發送端在發送資料時都以LEN為長度進行分包;2、尾部標記序列法,在每個要傳送的資料包的尾部設定一個特殊的位元組序列;3、頭部標記逐步接收法,定義一個使用者報頭,在報頭中註明每次發送的資料包大小。

tcp黏包問題怎麼處理?

tcp黏包問題處理的方法:

 1、設計方案一:定長發送

在進行資料傳送時採用固定長度的設計,也就是無論多大資料發送都分包為固定長度(為便於描述,此處定長為記為LEN),也就是發送端在發送資料時都以LEN為長度進行分包。這樣接收方都以固定的LEN進行接收,如此一來發送和接收就能一一對應了。分包的時候不一定能完整的剛好分成多個完整的LEN的包,最後一個包一般都會小於LEN,這時候最後一個包可以在不足的部分填充空白字節。

當然,這種方法會有缺陷。

1.最後一個套件的不足長度被填滿為空白部分,也即無效位元組序。那麼接收方可能難以辨別這無效的部分,它本身就是為了補位的,並無實際意義。這就為接收端處理其意義帶來了麻煩。當然也有解決辦法,可以透過增添標誌位的方法來彌補,即在每個資料包的最前面增加一個定長的報頭,然後將該資料包的末尾標記一併發送。接收方根據這個標記確認無效位元組序列,從而實現資料的完整接收。

2.在發送包長度隨機分佈的情況下,會造成頻寬浪費。例如發送長度可能為1,100,1000,4000位元組等等,則都需要按照定長最大值即4000來發送,資料包小於4000位元組的其他包也會被填充至4000,造成網路負載的無效浪費。

綜上,此方案適在傳送封包長度較為穩定(趨於某一固定值)的情況下有較好的效果。

相關學習推薦:PHP 程式設計從入門到精通

2、設計方案二:尾部標記序列

在每個要傳送的資料包的尾部設定一個特殊的位元組序列,此序列帶有特殊意義,跟字串的結束符標識”\0”一樣的含義,用來標示這個資料包的末尾,接收方可對接收的資料進行分析,透過尾部序列確認資料包的邊界。

這種方法的缺陷較為明顯:

1.接收方需要對資料進行分析,甄別尾部序列。

2.尾部序列的確定本身就是一個問題。什麼樣的序列可以像”\0”一樣來做一個結束符號呢?這個序列必須是不具備通常任何人類或程式可識別的帶含義的資料序列,就像「\0」是無效字串內容,因而可以作為字串的結束標記。那普通的網路通訊中,這個序列是什麼呢?我想一時間很難找到恰當的答案。

 

3、設計方案三:頭部標記逐步接收

這個方法是作者有限學識裡最好的方法了。它既不損失效率,也完美解決了任何大小的資料包的邊界問題。

這個方法的實作是這樣的,定義一個使用者報頭,在標頭中註明每次發送的資料包大小。接收者每次接收時先以報頭的size進行數據讀取,這必然只能讀到一個報頭的數據,從報頭中得到該數據包的數據大小,然後再按照此大小進行再次讀取,就能讀到數據的內容了。

這樣一來,每個資料包發送時都封裝一個報頭,然後接收方分兩次接收一個包,第一次接收報頭,根據報頭大小第二次才接收資料內容。 (此處的data[0]的本質是一個指針,指向資料的正文部分,也可以是一篇連續資料區的起始位置。因此可以設計成data[user_size],這樣的話。)

下面透過一張圖來展現設計想法。

 tcp黏包問題怎麼處理?

由圖看出,資料發送多了封裝標頭的動作;接收方將每個包的接收拆分成了兩次。

這方案看似精妙,實則也有缺陷:

1.報頭雖小,但每個包都需要多封裝sizeof(_data_head)的數據,積累效應也不可完全忽略。

2.接收方的接收動作分成了兩次,也就是進行資料讀取的操作被增加了一倍,而資料讀取操作的recv或read都是系統調用,這對核心而言的開銷是不能完全忽略的影響,對程式而言效能影響可忽略(系統呼叫的速度非常快)。

優點:避免了程式設計的複雜性,其有效性便於驗證,對軟體設計的穩定性要求來說更容易達標。綜上,方案三乃上上策!

以上是tcp黏包問題怎麼處理?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn