自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

你真的理解粘包與半包嗎?三分鐘搞懂它

開發(fā) 前端
只有等接收到全部包裹時,這個東西(傳輸的信息)才完整,所以半包情況下無法解析出完整的數據,需要等,等接收到全部包裹。

通俗的例子

這里先舉個可能不太恰當,但是很容易理解的例子。

比如,平時我們要寄快遞,如果東西太大的話,那么就需要拆成幾個包裹來郵寄。

收件人僅收到個別包裹的時候,東西是不完整的,對應到網絡傳輸中,這種情況就叫半包。

只有等接收到全部包裹時,這個東西(傳輸的信息)才完整,所以半包情況下無法解析出完整的數據,需要等,等接收到全部包裹。

那么問題來了,如何知曉已經收到全部包裹了呢?下文我們再作分析。

再比如,快過年了,我打算給家里的親戚送點禮物,給每位長輩送個手表,我們都知道手表的體積不大,并且我家里人都住在一個村,所以把給各長輩的禮物打包在一個包裹里郵寄,這樣能節(jié)省運費。

這種把本應該分多個包傳輸的數據合成一個包發(fā)送的情況,對應到網絡傳輸中,就叫粘包。

看完這個例子之后,應該對粘包與半包有點感覺了,接下來我們看下網絡中實際的情況。

實際情況

粘包與半包只有在 TCP 傳輸的時候才會有,像 UDP 是不會有這種情況的,原因是因為 TCP 是面向流的,數據之間沒有界限的,而 UDP 是有的界限的。

如果熟悉 TCP 和 UDP 報文格式的同學肯定知道,TCP 的包沒有報文長度,而 UDP 的包有報文長度,這也說明了 TCP 為什么是流式。

所以我為什么說上面的例子不太恰當,因為現實生活中快遞的包裹之間其實是有界限的,TCP 則像流水,沒有明確的界限。

然后 TCP 有發(fā)送緩沖區(qū)的概念,UDP 實際上是沒這個概念。

假設 TCP 一次傳輸的數據大小超過發(fā)送緩沖區(qū)大小,那么一個完整的報文就需要被拆分成兩個或更多的小報文,這可能會產生半包的情況,當接收端收到不完整的數據,是無法解析成功的。

如果 TCP 一次傳輸的數據大小小于發(fā)送緩沖區(qū),那么可能會跟別的報文合并起來一塊發(fā)送,這就是粘包。

此時接收端也無法正常解析報文,需要將其拆成多個正確的報文,才能正常解析。

關于粘包與半包,我還看到有拿 MTU (最大傳輸單元)說事的,如果發(fā)送的數據大于 MTU 那就會出現拆包,導致半包的情況。

我個人覺得這里有點不對,簡單理解下,UDP 也是要遵循 MTU 的呀,對吧?那它咋不會發(fā)生半包呢?

我們接著來看如何解決粘包與半包。

那如何解決粘包與半包問題呢?

  • 粘包:這個思路其實很清晰,就是把它拆開唄,具體就是看怎么拆了,比如我們可以固定長度,我們規(guī)定每個包都是10個字節(jié),那么就10個字節(jié)切一刀,這樣拆開解析就 ok 了。
  • 半包:半包其實就是信息還不完整,我們需要等接收到全部的信息之后再作處理,當我們識別這是一個不完整的包時候,我們先 hold 住,不作處理,等待數據完整再處理。這里關鍵點在于,我們如何才能知道此時完整了?上面說的固定長度其實也是一點,當然還有更多更好的解決方案,我們接著往下看。

實際常見解決粘包與半包問題有三個方案:

  • 固定長度
  • 分隔符
  • 固定長度字段+內容

為了說明方便,以下沒有按二進制的位等單位來描述。

固定長度

這個其實很簡單,比如現在要傳輸 ABC、EF 這兩個包,如果不做處理接收端很可能收到的是 AB、CEF 或者 ABCE、F 等等。

這時候我們固定長度,我們規(guī)定每個報文長度都是 3,如果一個報文實際數據不足 3,那么就用空字符填充一下 。

所以我們發(fā)送的報文是 :

接收到的情況可能是:

但我們是按照 3 位來處理的,所以一次只會按照 3 位來解析,所以第一次雖然收到的數據是 ABCE,但我們就解析 3 位,即解析出 ABC,留著了個 E,等我們要繼續(xù)解析 3 位的時候,發(fā)現長度不足 3,所以我們暫時先不管,先等等。

后面等到了 F“”,我們發(fā)現當下數據又滿足 3 位了,所以我們接著解析 EF“” 。

這樣就解決了粘包與半包問題。

對應到 Netty 中的實現就是 FixedLengthFrameDecoder,這個類來實現固定長度的解碼。

核心邏輯就是我上面說的,我們來看下源碼,很簡單:

固定長度的優(yōu)點:簡單。

缺點:固定長度很僵硬,不易于擴展,且如果設置過大來滿足業(yè)務場景的話,會導致空間浪費,因為不足長度的需要填充。

分隔符

這個應該很好理解, 還是拿 ABC、EF 這兩個包舉例,我在寫完 ABC后,插入一個分號,組成ABC;,EF 同理:

這樣以分隔符為界限來切分無界限的 TCP 流,來解決粘包與半包問題,這個應該很好理解,既然你 TCP 沒界限,我業(yè)務上給你搞個界限。

對應到 Netty 中的實現就是 DelimiterBasedFrameDecoder,具體源碼就不貼了,有點長,不過道理還是簡單的。

一直解析,等識別到分隔符之后,說明前面的數據完整了,于是解析前面的數據,然后繼續(xù)往后掃描解析。

分隔符的優(yōu)點:簡單,也不會浪費空間。

缺點:需要對內容本身進行處理,防止內容內出現分隔符,這樣就會導致錯亂,所以需要掃描一遍傳輸的數據將其轉義,或者可以用 base64 編碼數據,用 64 個之外的字符作為分隔符即可。

分隔符的處理方式在業(yè)界也是常用的,比如 Redis 就用換行符來分隔。

固定長度字段+內容

這個也很好理解,比如協議規(guī)定固定 4 位存放內容的長度,這樣內容就可以伸縮:

還是拿 ABC、EF 這兩個包舉例:

解析流程是:先獲取 4 位,如果當前收到的數據不夠 4 位,那就再等等,夠 4 位之后解析得到長度是 3,所以我再往后取 3 位,同樣數據如果不夠 3 位就再等等,夠了的話就解析,這樣就獲取一個完整的包了。

然后接著往后獲取 4 位,解析得到 2,同理根據 2 往后再取 2 位,解析得到 EF。

這種方式就是先解析固定長度的字段,獲得后面內容的長度,根據內容長度來獲取內容,從而得到一個完整的報文。

對應到 Netty 中的實現就是 LengthFieldBasedFrameDecoder,具體源碼就不貼了,有點長,

固定長度字段+內容的優(yōu)點:可以根據固定字段精準定位,也不用掃描轉義字符。

缺點:固定長度字段的設計比較困難,大了浪費空間,畢竟每個報文都帶這個長度,小了可能不夠用。

總結

好了,我們總結一下。

因為 TCP 是面向流的協議,且利用緩沖區(qū)來提高發(fā)送的效率,所以會導致粘包/半包情況的發(fā)生。

對于這種情況,我們可以在報文上動手腳,可以約定固定長度的報文,或埋入分隔符,或利用固定長度字段+內容等常見的三種方式來解決粘包、半包的問題。

以上三種在 Netty 中都有現成實現類,可直接使用:

FixedLengthFrameDecoder,固定長度

DelimiterBasedFrameDecoder,分隔符

LengthFieldBasedFrameDecoder,定長度字段+內容

建議實驗一下,會有更清晰的認識。

責任編輯:武曉燕 來源: yes的練級攻略
相關推薦

2024-01-16 07:46:14

FutureTask接口用法

2023-12-27 08:15:47

Java虛擬線程

2024-01-12 07:38:38

AQS原理JUC

2024-07-05 09:31:37

2023-12-04 18:13:03

GPU編程

2025-02-24 10:40:55

2023-01-31 08:24:55

HashMap死循環(huán)

2024-05-16 11:13:16

Helm工具release

2023-12-23 18:04:40

服務Eureka工具

2009-11-09 12:55:43

WCF事務

2024-12-18 10:24:59

代理技術JDK動態(tài)代理

2022-02-17 09:24:11

TypeScript編程語言javaScrip

2021-04-20 13:59:37

云計算

2024-08-30 08:50:00

2013-06-28 14:30:26

棱鏡計劃棱鏡棱鏡監(jiān)控項目

2020-06-30 10:45:28

Web開發(fā)工具

2023-11-23 19:26:43

2021-12-17 07:47:37

IT風險框架

2024-10-15 09:18:30

2021-02-03 14:31:53

人工智能人臉識別
點贊
收藏

51CTO技術棧公眾號