TCP中分包,黏包解決辦法
阿新 • • 發佈:2019-01-29
粘包產生原因:
先說TCP:由於TCP協議本身的機制(面向連線的可靠地協議-三次握手機制)客戶端與伺服器會維持一
個連線(Channel),資料在連線不斷開的情況下,可以持續不斷地將多個數據包發往伺服器,但是如
果傳送的網路資料包太小,那麼他本身會啟用Nagle演算法(可配置是否啟用)對較小的資料包進行合併
(基於此,TCP的網路延遲要UDP的高些)然後再發送(超時或者包大小足夠)。那麼這樣的話,伺服器
在接收到訊息(資料流)的時候就無法區分哪些資料包是客戶端自己分開發送的,這樣產生了粘包;服
務器在接收到資料庫後,放到緩衝區中,如果訊息沒有被及時從快取區取走,下次在取資料的時候可能
就會出現一次取出多個數據包的情況,造成粘包現象(確切來講,對於基於TCP協議的應用,不應用包
來描述,而應 用 流來描述),個人認為伺服器接收端產生的粘包應該與linux核心處理socket的方式
select輪詢機制的線性掃描頻度無關。
再說UDP:本身作為無連線的不可靠的傳輸協議(適合頻繁傳送較小的資料包),他不會對資料包進行
合併傳送(也就沒有Nagle演算法之說了),他直接是一端傳送什麼資料,直接就發出去了,既然他不會
對資料合併,每一個數據包都是完整的(資料+UDP頭+IP頭等等發一次資料封裝一次)也就沒有粘包一
說了。
分包產生的原因就簡單的多:可能是IP分片傳輸導致的,也可能是傳輸過程中丟失部分包導致出現的半
包,還有可能就是一個包可能被分成了兩次傳輸,在取資料的時候,先取到了一部分(還可能與接收的
緩衝區大小有關係),總之就是一個數據包被分成了多次接收。
解決辦法:
粘包與分包的處理方法:
我根據現有的一些開源資料做了如下總結(常用的解決方案):
一個是採用分隔符的方式,即我們在封裝要傳輸的資料包的時候,採用固定的符號作為結尾符(資料中
不能含結尾符),這樣我們接收到資料後,如果出現結尾標識,即人為的將粘包分開,如果一個包中沒
有出現結尾符,認為出現了分包,則等待下個包中出現後 組合成一個完整的資料包,這種方式適合於
文字傳輸的資料,如採用/r/n之類的分隔符;
另一種是採用在資料包中新增長度的方式,即在資料包中的固定位置封裝資料包的長度資訊(或可計算
資料包總長度的資訊),伺服器接收到資料後,先是解析包長度,然後根據包長度擷取資料包(此種方
式常出現於自定義協議中),但是有個小問題就是如果客戶端第一個資料包資料長度封裝的有錯誤,那
麼很可能就會導致後面接收到的所有資料包都解析出錯(由於TCP建立連線後流式傳輸機制),只有客
戶端關閉連線後重新開啟才可以消除此問題,我在處理這個問題的時候對資料長度做了校驗,會適時的
對接收到的有問題的包進行人為的丟棄處理(客戶端有自動重發機制,故而在應用層不會導致資料的不
完整性);
另一種不建議的方式是TCP採用短連線處理粘包(這個得根據需要來,所以不建議);
先說TCP:由於TCP協議本身的機制(面向連線的可靠地協議-三次握手機制)客戶端與伺服器會維持一
個連線(Channel),資料在連線不斷開的情況下,可以持續不斷地將多個數據包發往伺服器,但是如
果傳送的網路資料包太小,那麼他本身會啟用Nagle演算法(可配置是否啟用)對較小的資料包進行合併
(基於此,TCP的網路延遲要UDP的高些)然後再發送(超時或者包大小足夠)。那麼這樣的話,伺服器
在接收到訊息(資料流)的時候就無法區分哪些資料包是客戶端自己分開發送的,這樣產生了粘包;服
務器在接收到資料庫後,放到緩衝區中,如果訊息沒有被及時從快取區取走,下次在取資料的時候可能
就會出現一次取出多個數據包的情況,造成粘包現象(確切來講,對於基於TCP協議的應用,不應用包
來描述,而應 用 流來描述),個人認為伺服器接收端產生的粘包應該與linux核心處理socket的方式
select輪詢機制的線性掃描頻度無關。
再說UDP:本身作為無連線的不可靠的傳輸協議(適合頻繁傳送較小的資料包),他不會對資料包進行
合併傳送(也就沒有Nagle演算法之說了),他直接是一端傳送什麼資料,直接就發出去了,既然他不會
對資料合併,每一個數據包都是完整的(資料+UDP頭+IP頭等等發一次資料封裝一次)也就沒有粘包一
說了。
分包產生的原因就簡單的多:可能是IP分片傳輸導致的,也可能是傳輸過程中丟失部分包導致出現的半
包,還有可能就是一個包可能被分成了兩次傳輸,在取資料的時候,先取到了一部分(還可能與接收的
緩衝區大小有關係),總之就是一個數據包被分成了多次接收。
解決辦法:
粘包與分包的處理方法:
我根據現有的一些開源資料做了如下總結(常用的解決方案):
一個是採用分隔符的方式,即我們在封裝要傳輸的資料包的時候,採用固定的符號作為結尾符(資料中
不能含結尾符),這樣我們接收到資料後,如果出現結尾標識,即人為的將粘包分開,如果一個包中沒
有出現結尾符,認為出現了分包,則等待下個包中出現後 組合成一個完整的資料包,這種方式適合於
文字傳輸的資料,如採用/r/n之類的分隔符;
另一種是採用在資料包中新增長度的方式,即在資料包中的固定位置封裝資料包的長度資訊(或可計算
資料包總長度的資訊),伺服器接收到資料後,先是解析包長度,然後根據包長度擷取資料包(此種方
式常出現於自定義協議中),但是有個小問題就是如果客戶端第一個資料包資料長度封裝的有錯誤,那
麼很可能就會導致後面接收到的所有資料包都解析出錯(由於TCP建立連線後流式傳輸機制),只有客
戶端關閉連線後重新開啟才可以消除此問題,我在處理這個問題的時候對資料長度做了校驗,會適時的
對接收到的有問題的包進行人為的丟棄處理(客戶端有自動重發機制,故而在應用層不會導致資料的不
完整性);
另一種不建議的方式是TCP採用短連線處理粘包(這個得根據需要來,所以不建議);