怎么解決TCP網路傳輸「粘包」問題?

TCP粘包是指發送方發送的多個數據包到接收方后粘連在一起,導致數據包不能完整的提現發送的數據。

TCP協議

TCP是一個面向連接的傳輸層協議,不屬于ISO制定的協議集。TCP協議在商業界和工業界的成功應用,使它成為事實上的網路標準,廣泛應用于各種網路主機間的通信。

TCP目標是為用戶提供可靠的端到端連接,保證資訊有序無誤的傳輸。TCP為確保可靠性采用了數據編號、校驗和計算、數據確認等一系列措施。

TCP對傳送的每個數據字節都進行編號,并請求接收方回傳確認資訊(ACK)。發送方如果在規定的時間內沒有收到數據確認,就重傳該數據。

  • 數據編號使接收方能夠處理數據的失序和重復問題。
  • 數據誤碼問題通過在每個傳輸的數據段中增加校驗和予以解決,接收方在接收到數據后檢查校驗和,若校驗和有誤,則丟棄該有誤碼的數據段,并要求發送方重傳。
  • 流量控制也是保證可靠性的一個重要措施,若無流控,可能會因接收緩沖區溢出而丟失大量數據,導致許多重傳,造成網路擁塞惡性循環。
  • TCP采用可變窗口進行流量控制,由接收方控制發送方發送的數據量。

這些可靠性保障措施為用戶提供了高可靠性的網路傳輸服務,但也影響了傳輸效率。在實際工程應用中,只有關鍵數據的傳輸才采用TCP,而普通數據的傳輸一般采用高效率的UDP。

UDP不會出現粘包問題。UDP支持的是一對多的模式,不會使用塊的合并優化算法,所以接收端的skbuff(套接字緩沖區)采用了鏈式結構來記錄每一個到達的UDP包,在每個UDP包中就有了消息頭(包含消息來源地址,端口等資訊),接收端很容易就能進行區分處理了。

粘包出現原因

出現粘包現象的原因有很多方面,它既可能由發送方造成的,也可能是由接收方造成的。

發送方原因

TCP需要盡可能高效和可靠,默認采用Nagle算法,發送方往往要收集到足夠多的數據后合并相連的小數據包,才發送一包數據,這樣接收方就收到了粘包數據。但接收方并不知曉發送方合并數據包,并數據包的合并在TCP協議中是沒有分界線的,就會導致接收方不能還原其本來的數據包。

接收方原因

TCP是基于“流”的。網路傳輸數據的速度可能會快過接收方處理數據的速度,這時候就會導致,接收方在讀取緩沖區時,緩沖區存在多個數據包。在TCP協議中接收方是一次讀取緩沖區中的所有內容,就不能反映原本的數據資訊。

粘包情況有兩種:

一種是粘在一起的包都是完整的數據包;

一種是粘在一起的包有不完整的包;

不是所有的粘包現象都需要處理

如果傳輸的數據為不帶結構的連續流數據(如文件傳輸),就不必把粘連的包分開(簡稱分包)。但實際工程應用中一般為帶結構的數據,這時就需要做分包處理。

在處理定長結構數據的粘包問題時,分包算法比較簡單;

在處理不定長結構數據的粘包問題時,分包算法就比較復雜。

特別是粘在一起的包有不完整的包的粘包情況,一包數據內容被分在了兩個連續的接收包中,處理起來難度較大。實際工程應用中應盡量避免出現粘包現象。

為了避免粘包現象,可采取以下幾種措施:

(1)發送方引起的粘包可通過編程設置來避免。如:PUSH標志是TCP提供了強制數據立即傳送的操作指令,TCP軟體收到該操作指令后,就立即將本段數據發送出去,而不必等待發送緩沖區滿。

缺點:雖然可以避免發送方引起的粘包,但關閉了Negle優化算法,降低了網路發送效率,影響應用程式的性能,一般不建議使用。

(2)接收方引起的粘包,可通過優化程式設計、精簡接收進程工作量、提高接收進程優先級等措施來及時接收數據,盡量避免出現粘包現象。

缺點:只能減少出現粘包的可能性,但并不能完全避免粘包,當發送頻率較高或某個時間段數據包到達接收方較快,接收方還是有可能來不及接收,導致粘包。

(3)由接收方控制,將一包數據按結構字段,人為控制分多次接收,然后合并,通過這種手段來避免粘包。

缺點:應用程式的效率較低,對實時應用的場合不適合。

一種比較周全的對策是:接收方創建一預處理線程,對接收到的數據包進行預處理,將粘連的包分開。另外,普通數據的傳輸采用UDP,而重要的數據采用TCP。由于UDP不是面向‘流’的,而且UDP是具有消息邊界的。也就是說UDP的發送的每一個數據包都是獨立的。所以UDP并不存在粘包的問題。

以上個人淺見,歡迎批評指正。喜歡的可以關注我,謝謝!

認同我的看法的請點個贊再走,再次感謝!

10 条回复 A文章作者 M管理員
  1. 定義一個包頭不就行了嗎,固定大小的包頭

  2. 現在基本都是采用固定大小頭部資訊來解決

  3. 我喜歡粘豆包

  4. 通常的協議是先在最前定義一個數據大小,后續你接收的數據不管怎么分包,按此大小進行合并即可!

  5. 呵呵,tcp是數據流。udp才是包。你可以理解水管放水,只有關了閘流才停止。

  6. 粘包本身就是一個偽問題。tcp傳的是數據流,怎么分析數據接收方要按自定協議處理好。所以首先要訂好協議,然后按協議正確處理。就不存在所謂粘包問題。

  7. 怎么會粘?沒有報頭還叫tcp包嗎?報頭有ack,syn字段的,你收到的包的字節數已經在syn字段里標明了。如果收了以后查收不符合syn標稱數量,包就自動丟包了,發res回復過去要求重新發送同一個包。怎么可能粘?每一個包的長度都是嚴格定好的。

  8. 可采用如下方案:\n1.文本消息,用分隔符\n2. 消息定長\n3.加消息頭,指定消息長度

  9. 哈哈,這回答就別發出來了

  10. tcp本身沒有包的概念,這個問題是對tcp協議的錯誤理解