Socket 粘包和分包問題

許佳佳233發表於2018-10-20

概念

Socket通訊時會對傳送的位元組資料進行分包和粘包處理,屬於一種Socket內部的優化機制。
粘包:
當傳送的位元組資料包比較小且頻繁傳送時,Socket內部會將位元組資料進行粘包處理,既將頻繁傳送的小位元組資料打包成 一個整包進行傳送,降低記憶體的消耗。
分包:
當傳送的位元組資料包比較大時,Socket內部會將傳送的位元組資料進行分包處理,降低記憶體和效能的消耗。

例子解釋

當前傳送方傳送了兩個包,兩個包的內容如下:
123456789
ABCDEFGH

我們希望接收方的情況是:收到兩個包,第一個包為:123456789,第二個包為:ABCDEFGH。但是在粘包和分包出現的情況就達不到預期情況。

粘包情況

兩個包在很短的時間間隔內傳送,比如在0.1秒內傳送了這兩個包,如果包長度足夠的話,那麼接收方只會接收到一個包,如下:

123456789ABCDEFGH

分包情況

假設包的長度最長設定為5位元組(較極端的假設,一般長度設定為1000到1500之間),那麼在沒有粘包的情況下,接收方就會收到4個包,如下:

12345
6789
ABCDE
FGH

處理方式

因為存在粘包和分包的情況,所以接收方需要對接收的資料進行一定的處理,主要解決的問題有兩個:

  1. 在粘包產生時,要可以在同一個包內獲取出多個包的內容。
  2. 在分包產生時,要保留上一個包的部分內容,與下一個包的部分內容組合。

目前處理方式主要兩種:

一、給資料包的頭尾加上標記。

比如在資料包的頭部加上“START”字串,尾部加上"END"字串,這樣可以解析出START和END之間的字串就是接收方需要接收的內容。(當然真正處理的時候不可能使用START和END這種混效率較高的字串,此處只是個例子)
上邊兩個包的例子就可以如下:

START123456789END
STARTABCDEFGHEND

二、在資料包頭部加上內容的長度

傳送方在傳送的時候就可以在包頭加上包的長度,接收方每次接收的時候都根據頭部的長度去獲取後面的內容。
上邊兩個包的例子就可以如下:

PACKAGELENGTH:0009123456789
PACKAGELENGTH:0008ABCDEFGH

處理例子

頭尾標記處理

粘包

START123456789ENDSTARTABCDEFGHEND

獲取第一個START和第一個END的位置,然後獲取他們之間的內容,第二個包的內容就是獲取第二個START和第二個END的位置。

分包

START1234567
89END

每個包要判斷最後是否是END結尾,如果沒有找到END,那麼就保留上一個包START之後的內容,與下一個包第一個END之前的內容組合。

頭部長度處理

粘包

PACKAGELENGTH:0009123456789PACKAGELENGTH:0008ABCDEFGH

獲取“PACKAGELENGTH:”這個字串後面4個字元,轉化為數字就是包的長度,根據包的長度獲取後面的內容,第二個內容的長度就是獲取第二個“PACKAGELENGTH:”字串後面的4個字元。

分包

PACKAGELENGTH:0009123456
789

獲取“PACKAGELENGTH:”這個字串後面4個字元,轉化為數字就是包的長度,如果包結尾還沒有獲取完,那麼就要獲取下一個包前面的部分內容。

部分細節情況

看了前面的例子,比較善於思考的讀者肯定已經想到了一些其他問題,這些問題處理起來方式和上面相似,筆者在此羅列一下,就不重複解釋了,相信聰明的讀者能夠自己解決:

1、粘包和分包問題一起出現

START123456789ENDSTARTAB
CDEFGHEND

2、頭尾標誌由於分包獲取不完整

START123456789E
ND

相關文章