溫馨提示×

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

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

TCP粘拆包問(wèn)題及Netty中的解決方案是什么

發(fā)布時(shí)間:2021-12-07 11:05:56 來(lái)源:億速云 閱讀:171 作者:柒染 欄目:大數(shù)據(jù)

TCP粘拆包問(wèn)題及Netty中的解決方案是什么,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

熟悉 TCP 編程的都知道,無(wú)論是服務(wù)端還是客戶(hù)端,當(dāng)我們讀取或者發(fā)送消息的時(shí)候,都需要考慮 TCP 底層的粘包/拆包機(jī)制

TCP 粘包/拆包問(wèn)題,在功能測(cè)試時(shí)往往不會(huì)怎么出現(xiàn),而一旦并發(fā)壓力上來(lái),或者發(fā)送大報(bào)文之后,就很容易出現(xiàn)粘包/拆包問(wèn)題。如果代碼沒(méi)有考慮,往往就會(huì)出現(xiàn)解碼錯(cuò)位或者錯(cuò)誤,導(dǎo)致程序不能正常工作。

本篇文章,我們先簡(jiǎn)單了解 TCP 粘包/拆包 的基礎(chǔ)知識(shí),然后來(lái)看看 Netty 是如何解決這個(gè)問(wèn)題的。

TCP 粘包/拆包

TCP 粘包/拆包問(wèn)題說(shuō)明

TCP 是個(gè) “流” 協(xié)議。所謂流,就是沒(méi)有界限的一串?dāng)?shù)據(jù)。TCP 底層并不了解上層(如 HTTP 協(xié)議)業(yè)務(wù)數(shù)據(jù)的具體含義,它會(huì)根據(jù) TCP 緩沖區(qū)的實(shí)際情況進(jìn)行包的劃分,所以在業(yè)務(wù)上認(rèn)為,一個(gè)完整的包可能會(huì)被 TCP 拆分成多個(gè)包進(jìn)行發(fā)送,也有可能把多個(gè)小的包封裝成一個(gè)大的數(shù)據(jù)包發(fā)送,這就是所謂的 TCP 粘包和拆包問(wèn)題。我們可以通過(guò)下面的示例圖,對(duì) TCP 粘包和拆包問(wèn)題進(jìn)行說(shuō)明。

TCP粘拆包問(wèn)題及Netty中的解決方案是什么

假設(shè)客戶(hù)端依次發(fā)送了兩個(gè)數(shù)據(jù)包 DI 和 D2 給服務(wù)端,由于服務(wù)端一次讀取到的字節(jié)數(shù)是不確定的,故可能存在以下 4 種情況。

1.服務(wù)端分兩次讀取到了兩個(gè)獨(dú)立的數(shù)據(jù)包,分別是 D1 和 D2,沒(méi)有粘包和拆包;2.服務(wù)端一次接收到了兩個(gè)數(shù)據(jù)包,DI 和 D2 粘合在一起,被稱(chēng)為 TCP 粘包;3.服務(wù)端分兩次讀取到了兩個(gè)數(shù)據(jù)包,第一次讀取到了完整的 DI 包 和 D2 包的部分內(nèi)容,第二次讀取到了 D2 包的剩余內(nèi)容,這被稱(chēng)為 TCP 拆包;4.服務(wù)端分兩次讀取到了兩個(gè)數(shù)據(jù)包,第一次讀取到了 D1 包的部分內(nèi)容,第二次讀取到了 D1 包的剩余內(nèi)容和 D2 包的整包。

如果此時(shí)服務(wù)端 TCP 接收滑窗非常小,而 數(shù)據(jù)包 DI 和 D2 比較大,很有可能會(huì)發(fā)生第 5 種可能,即服務(wù)端分多次才能將 D1 和 D2 包接收完全,期間發(fā)生多次拆包。

TCP 粘包/拆包發(fā)生的原因

問(wèn)題產(chǎn)生的原因有三個(gè),分別如下:

1.應(yīng)用程序 write 寫(xiě)入的字節(jié)大小 超出了 套接口發(fā)送緩沖區(qū)大??;2.進(jìn)行 MSS 大小的 TCP 分段;3.以太網(wǎng)幀的 payload 大于 MTU 進(jìn)行 IP 分片。

粘拆包問(wèn)題的解決策略

由于底層的 TCP 無(wú)法理解上層的業(yè)務(wù)數(shù)據(jù),所以在底層是無(wú)法保證數(shù)據(jù)包不被拆分和重組的,這個(gè)問(wèn)題只能通過(guò)上層的應(yīng)用協(xié)議棧設(shè)計(jì)來(lái)解決,根據(jù)業(yè)界的主流協(xié)議的解決方案,可以歸納如下。

1.固定消息長(zhǎng)度,例如,每個(gè)報(bào)文的大小為 固定長(zhǎng)度 200 字節(jié),如果不夠,空位補(bǔ)空格;2.在包尾使用 “回車(chē)換行符” 等特殊字符,作為消息結(jié)束的標(biāo)志,例如 FTP 協(xié)議,這種方式在文本協(xié)議中應(yīng)用比較廣泛;3.將消息分為消息頭和消息體,在消息頭中定義一個(gè) 長(zhǎng)度字段 Len 來(lái)標(biāo)識(shí)消息的總長(zhǎng)度;4.更復(fù)雜的應(yīng)用層協(xié)議。

介紹完了 TCP 粘包/拆包 的基礎(chǔ),下面我們來(lái)看看 Netty 是如何使用一系列 “半包解碼器” 來(lái)解決 TCP 粘包/拆包問(wèn)題的。

Netty 解碼器解決 TCP 粘拆包問(wèn)題

根據(jù)上面的粘拆包問(wèn)題解決策略,Netty 提供了相應(yīng)的解碼器實(shí)現(xiàn)。有了這些解碼器,用戶(hù)不需要自己對(duì)讀取的報(bào)文進(jìn)行人工解碼,也不需要考慮 TCP 的粘包和拆包。

LineBasedFrameDecoder 和 StringDecoder 的原理分析

為了解決 TCP 粘包 / 拆包 導(dǎo)致的半包讀寫(xiě)問(wèn)題,Netty 默認(rèn)提供了多種編解碼器用于處理半包,只要能熟練掌握這些類(lèi)庫(kù)的使用,TCP 粘拆包問(wèn)題從此會(huì)變得非常容易,你甚至不需要關(guān)心它們,這也是其他 NIO 框架 和 JDK 原生的 NIO API 所無(wú)法匹敵的。對(duì)于使用者來(lái)說(shuō),只要將支持半包解碼的 Handler 添加到 ChannelPipeline 對(duì)象 中即可,不需要寫(xiě)額外的代碼,使用起來(lái)非常簡(jiǎn)單。

// 示例代碼,其中 socketChannel 是一個(gè) SocketChannel對(duì)象socketChannel.pipeline().addLast( new LineBasedFrameDecoder(1024) );socketChannel.pipeline().addLast( new StringDecoder() );

LineBasedFrameDecoder 的工作原理是它依次遍歷 ByteBuf 中的可讀字節(jié),判斷看是否有 \n 或者 \r\n,如果有,就以此位置為結(jié)束位置,從可讀索引到結(jié)束位置區(qū)間的字節(jié)就組成了一行。它是以換行符為結(jié)束標(biāo)志的解碼器,支持?jǐn)y帶結(jié)束符或者不攜帶結(jié)束符兩種解碼方式,同時(shí)支持配置單行的最大長(zhǎng)度。如果連續(xù)讀取到最大長(zhǎng)度后仍然沒(méi)有發(fā)現(xiàn)換行符,就會(huì)拋出異常,同時(shí)忽略掉之前讀到的異常碼流。

StringDecoder 的功能非常簡(jiǎn)單,就是將接收到的對(duì)象轉(zhuǎn)換成字符串,然后繼續(xù)調(diào)用后面的 Handler。LineBasedFrameDecoder + StringDecoder 組合 就是按行切換的文本解碼器,它被設(shè)計(jì)用來(lái)支持 TCP 的粘包和拆包。

其它解碼器

除了 LineBasedFrameDecoder 以外,還有兩個(gè)常用的解碼器 DelimiterBasedFrameDecoder 和 FixedLengthFrameDecoder,前者能自動(dòng)對(duì) “以分隔符做結(jié)束標(biāo)志的消息” 進(jìn)行解碼,后者可以自動(dòng)完成對(duì)定長(zhǎng)消息的解碼。使用方法也和前面的示例代碼相同,結(jié)合字符串解碼器 StringDecoder,輕松完成對(duì)很多消息的自動(dòng)解碼。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問(wèn)一下細(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