溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Netty中粘包和半包產(chǎn)生原因有哪些

發(fā)布時(shí)間:2021-12-28 15:32:27 來源:億速云 閱讀:108 作者:小新 欄目:大數(shù)據(jù)

這篇文章主要介紹了Netty中粘包和半包產(chǎn)生原因有哪些,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

定義

TCP 傳輸中,客戶端發(fā)送數(shù)據(jù),實(shí)際是把數(shù)據(jù)寫入到了 TCP 的緩存中,粘包和半包也就會(huì)在此時(shí)產(chǎn)生。

客戶端給服務(wù)端發(fā)送了兩條消息ABCDEF,服務(wù)端這邊的接收會(huì)有多少種情況呢?有可能是一次性收到了所有的消息ABCDEF,有可能是收到了三條消息AB、CD、EF。

上面所說的一次性收到了所有的消息ABCDEF,類似于粘包。如果客戶端發(fā)送的包的大小比 TCP 的緩存容量小,并且 TCP 緩存可以存放多個(gè)包,那么客戶端和服務(wù)端的一次通信就可能傳遞了多個(gè)包,這時(shí)候服務(wù)端從 TCP 緩存就可能一下讀取了多個(gè)包,這種現(xiàn)象就叫粘包。

上面說的后面那種收到了三條消息AB、CD、EF,類似于半包。如果客戶端發(fā)送的包的大小比 TCP 的緩存容量大,那么這個(gè)數(shù)據(jù)包就會(huì)被分成多個(gè)包,通過 Socket 多次發(fā)送到服務(wù)端,服務(wù)端第一次從接受緩存里面獲取的數(shù)據(jù),實(shí)際是整個(gè)包的一部分,這時(shí)候就產(chǎn)生了半包(半包不是說只收到了全包的一半,是說收到了全包的一部分)。

產(chǎn)生原因

其實(shí)從上面的定義,我們就可以大概知道產(chǎn)生的原因了。

粘包的主要原因:

  1. 發(fā)送方每次寫入數(shù)據(jù) < 套接字(Socket)緩沖區(qū)大小

  2. 接收方讀取套接字(Socket)緩沖區(qū)數(shù)據(jù)不夠及時(shí)

半包的主要原因:

  1. 發(fā)送方每次寫入數(shù)據(jù) > 套接字(Socket)緩沖區(qū)大小

  2. 發(fā)送的數(shù)據(jù)大于協(xié)議的 MTU (Maximum Transmission Unit,最大傳輸單元),因此必須拆包

其實(shí)我們可以換個(gè)角度看待問題:

  1. 收發(fā)的角度看,便是一個(gè)發(fā)送可能被多次接收,多個(gè)發(fā)送可能被一次接收。

  2. 傳輸的角度看,便是一個(gè)發(fā)送可能占用多個(gè)傳輸包,多個(gè)發(fā)送可能共用一個(gè)傳輸包。

根本原因,其實(shí)是

TCP 是流式協(xié)議,消息無邊界。

(PS :UDP 雖然也可以一次傳輸多個(gè)包或者多次傳輸一個(gè)包,但每個(gè)消息都是有邊界的,因此不會(huì)有粘包和半包問題。)

解決方法

就像上面說的,UDP 之所以不會(huì)產(chǎn)生粘包和半包問題,主要是因?yàn)橄⒂羞吔?,因此,我們也可以采取類似的思路?/p>

改成短連接

將 TCP 連接改成短連接,一個(gè)請(qǐng)求一個(gè)短連接。這樣的話,建立連接到釋放連接之間的消息即為傳輸?shù)男畔?,消息也就產(chǎn)生了邊界。

這樣的方法就是十分簡(jiǎn)單,不需要在我們的應(yīng)用中做過多修改。但缺點(diǎn)也就很明顯了,效率低下,TCP 連接和斷開都會(huì)涉及三次握手以及四次握手,每個(gè)消息都會(huì)涉及這些過程,十分浪費(fèi)性能。

因此,并不推介這種方式。

封裝成幀

封裝成幀(Framing),也就是原本發(fā)送消息的單位是緩沖大小,現(xiàn)在換成了幀,這樣我們就可以自定義邊界了。一般有4種方式:

固定長(zhǎng)度

這種方式下,消息邊界也就是固定長(zhǎng)度即可。

優(yōu)點(diǎn)就是實(shí)現(xiàn)很簡(jiǎn)單,缺點(diǎn)就是空間有極大的浪費(fèi),如果傳遞的消息中大部分都比較短,這樣就會(huì)有很多空間是浪費(fèi)的。

因此,這種方式一般也是不推介的。

分隔符

這種方式下,消息邊界也就是分隔符本身。

優(yōu)點(diǎn)是空間不再浪費(fèi),實(shí)現(xiàn)也比較簡(jiǎn)單。缺點(diǎn)是當(dāng)內(nèi)容本身出現(xiàn)分割符時(shí)需要轉(zhuǎn)義,所以無論是發(fā)送還是接受,都需要進(jìn)行整個(gè)內(nèi)容的掃描。

因此,這種方式效率也不是很高,但可以嘗試使用。

專門的 length 字段

這種方式,就有點(diǎn)類似 Http 請(qǐng)求中的 Content-Length,有一個(gè)專門的字段存儲(chǔ)消息的長(zhǎng)度。作為服務(wù)端,接受消息時(shí),先解析固定長(zhǎng)度的字段(length字段)獲取消息總長(zhǎng)度,然后讀取后續(xù)內(nèi)容。

優(yōu)點(diǎn)是精確定位用戶數(shù)據(jù),內(nèi)容也不用轉(zhuǎn)義。缺點(diǎn)是長(zhǎng)度理論上有限制,需要提前限制可能的最大長(zhǎng)度從而定義長(zhǎng)度占用字節(jié)數(shù)。

因此,十分推介用這種方式。

其他方式

其他方式就各不相同了,比如 JSON 可以看成是使用{}是否成對(duì)。這些優(yōu)缺點(diǎn)就需要大家在各自的場(chǎng)景中進(jìn)行衡量了。

Netty 中的實(shí)現(xiàn)

Netty 支持上文所講的封裝成幀(Framing)中的前三種方式,簡(jiǎn)單介紹下:

方式解碼編碼
固定長(zhǎng)度FixedLengthFrameDecoder簡(jiǎn)單
分割符DelimiterBasedFrameDecoder簡(jiǎn)單
專門的 length 字段LengthFieldBasedFrameDecoderLengthFieldPrepender

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Netty中粘包和半包產(chǎn)生原因有哪些”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI