Netty如何解決TCP粘包半包難題?
? 點(diǎn)擊上方“JavaEdge”,關(guān)注公眾號(hào)

1 TCP為何會(huì)有粘包半包?

1.1 粘包

發(fā)送方每次寫(xiě)入數(shù)據(jù) < 套接字緩沖區(qū)大小
接收方讀取套接字緩沖區(qū)數(shù)據(jù)不夠及時(shí)
1.2 半包

發(fā)送方寫(xiě)入數(shù)據(jù) > 套接字緩沖區(qū)大小
發(fā)送的數(shù)據(jù)大于協(xié)議的MTU ( Maximum Transmission Unit,最大傳輸單元),必須拆包
而且
一個(gè)發(fā)送可能被多次接收,多個(gè)發(fā)送可能被一次接收
一個(gè)發(fā)送可能占用多個(gè)傳輸包,多個(gè)發(fā)送可能公用一個(gè)傳輸包
TCP 本質(zhì)上是流式協(xié)議,消息無(wú)邊界。
而UDP就像快遞,雖然一次運(yùn)輸多個(gè),但每個(gè)包都有邊界,一個(gè)個(gè)簽收
所以無(wú)此類(lèi)問(wèn)題。
清楚了問(wèn)題產(chǎn)生的本質(zhì),那么就知道如何避免了,即確定消息的邊界。

2 解決方案

2.1 改為短連接

一個(gè)請(qǐng)求一個(gè)短連接。建立連接到釋放連接之間的信息即為傳輸信息。
簡(jiǎn)單,但效率低下,不推薦。
2.2 封裝成幀


2.2.1 固定長(zhǎng)度

解碼:FixedLengthFrameDecoder

滿足固定長(zhǎng)度即可。

簡(jiǎn)單,但空間浪費(fèi),不推薦。

2.2.2 分割符

解碼:DelimiterBasedFrameDecoder,分隔符之間即為消息。

空間不浪費(fèi),也比較簡(jiǎn)單,但內(nèi)容本身出現(xiàn)分隔符時(shí)需轉(zhuǎn)義,所以需掃描內(nèi)容。
推薦度低。

2.2.3 固定長(zhǎng)度字段存?zhèn)€內(nèi)容的長(zhǎng)度信息

解碼:LengthFieldBasedFrameDecoder
編碼:LengthFieldPrepender
先解析固定長(zhǎng)度的字段獲取長(zhǎng)度,然后讀取后續(xù)內(nèi)容。這就沒(méi)有之前的缺點(diǎn)了
精確定位用戶數(shù)據(jù),內(nèi)容也不用轉(zhuǎn)義。
但長(zhǎng)度理論上有限制,需提前預(yù)知可能的最大長(zhǎng)度,從而定義長(zhǎng)度占用字節(jié)數(shù)。
如果直接定義成最大長(zhǎng)度,但實(shí)際上每次傳輸?shù)挠诌h(yuǎn)沒(méi)達(dá)到最大值,不就浪費(fèi)空間啦,所以根據(jù)需要設(shè)置最大長(zhǎng)度。
重點(diǎn)推薦使用。
其他方式比如 json 可看{}是否已經(jīng)成對(duì)。但這種明顯要掃描全部?jī)?nèi)容才知道是否成對(duì),性能較低。
往期推薦

目前交流群已有?800+人,旨在促進(jìn)技術(shù)交流,可關(guān)注公眾號(hào)添加筆者微信邀請(qǐng)進(jìn)群
喜歡文章,點(diǎn)個(gè)“在看、點(diǎn)贊、分享”素質(zhì)三連支持一下~
