您好,登錄后才能下訂單哦!
TCP粘拆包問題及Netty中的解決方案是什么,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
熟悉 TCP 編程的都知道,無論是服務端還是客戶端,當我們讀取或者發送消息的時候,都需要考慮 TCP 底層的粘包/拆包機制。
TCP 粘包/拆包問題,在功能測試時往往不會怎么出現,而一旦并發壓力上來,或者發送大報文之后,就很容易出現粘包/拆包問題。如果代碼沒有考慮,往往就會出現解碼錯位或者錯誤,導致程序不能正常工作。
本篇文章,我們先簡單了解 TCP 粘包/拆包 的基礎知識,然后來看看 Netty 是如何解決這個問題的。
TCP 是個 “流” 協議。所謂流,就是沒有界限的一串數據。TCP 底層并不了解上層(如 HTTP 協議)業務數據的具體含義,它會根據 TCP 緩沖區的實際情況進行包的劃分,所以在業務上認為,一個完整的包可能會被 TCP 拆分成多個包進行發送,也有可能把多個小的包封裝成一個大的數據包發送,這就是所謂的 TCP 粘包和拆包問題。我們可以通過下面的示例圖,對 TCP 粘包和拆包問題進行說明。
假設客戶端依次發送了兩個數據包 DI 和 D2 給服務端,由于服務端一次讀取到的字節數是不確定的,故可能存在以下 4 種情況。
1.服務端分兩次讀取到了兩個獨立的數據包,分別是 D1 和 D2,沒有粘包和拆包;2.服務端一次接收到了兩個數據包,DI 和 D2 粘合在一起,被稱為 TCP 粘包;3.服務端分兩次讀取到了兩個數據包,第一次讀取到了完整的 DI 包 和 D2 包的部分內容,第二次讀取到了 D2 包的剩余內容,這被稱為 TCP 拆包;4.服務端分兩次讀取到了兩個數據包,第一次讀取到了 D1 包的部分內容,第二次讀取到了 D1 包的剩余內容和 D2 包的整包。
如果此時服務端 TCP 接收滑窗非常小,而 數據包 DI 和 D2 比較大,很有可能會發生第 5 種可能,即服務端分多次才能將 D1 和 D2 包接收完全,期間發生多次拆包。
問題產生的原因有三個,分別如下:
1.應用程序 write 寫入的字節大小 超出了 套接口發送緩沖區大小;2.進行 MSS 大小的 TCP 分段;3.以太網幀的 payload 大于 MTU 進行 IP 分片。
由于底層的 TCP 無法理解上層的業務數據,所以在底層是無法保證數據包不被拆分和重組的,這個問題只能通過上層的應用協議棧設計來解決,根據業界的主流協議的解決方案,可以歸納如下。
1.固定消息長度,例如,每個報文的大小為 固定長度 200 字節,如果不夠,空位補空格;2.在包尾使用 “回車換行符” 等特殊字符,作為消息結束的標志,例如 FTP 協議,這種方式在文本協議中應用比較廣泛;3.將消息分為消息頭和消息體,在消息頭中定義一個 長度字段 Len 來標識消息的總長度;4.更復雜的應用層協議。
介紹完了 TCP 粘包/拆包 的基礎,下面我們來看看 Netty 是如何使用一系列 “半包解碼器” 來解決 TCP 粘包/拆包問題的。
根據上面的粘拆包問題解決策略,Netty 提供了相應的解碼器實現。有了這些解碼器,用戶不需要自己對讀取的報文進行人工解碼,也不需要考慮 TCP 的粘包和拆包。
為了解決 TCP 粘包 / 拆包 導致的半包讀寫問題,Netty 默認提供了多種編解碼器用于處理半包,只要能熟練掌握這些類庫的使用,TCP 粘拆包問題從此會變得非常容易,你甚至不需要關心它們,這也是其他 NIO 框架 和 JDK 原生的 NIO API 所無法匹敵的。對于使用者來說,只要將支持半包解碼的 Handler 添加到 ChannelPipeline 對象 中即可,不需要寫額外的代碼,使用起來非常簡單。
// 示例代碼,其中 socketChannel 是一個 SocketChannel對象socketChannel.pipeline().addLast( new LineBasedFrameDecoder(1024) );socketChannel.pipeline().addLast( new StringDecoder() );
LineBasedFrameDecoder
的工作原理是它依次遍歷 ByteBuf 中的可讀字節,判斷看是否有 \n
或者 \r\n
,如果有,就以此位置為結束位置,從可讀索引到結束位置區間的字節就組成了一行。它是以換行符為結束標志的解碼器,支持攜帶結束符或者不攜帶結束符兩種解碼方式,同時支持配置單行的最大長度。如果連續讀取到最大長度后仍然沒有發現換行符,就會拋出異常,同時忽略掉之前讀到的異常碼流。
StringDecoder
的功能非常簡單,就是將接收到的對象轉換成字符串,然后繼續調用后面的 Handler。LineBasedFrameDecoder
+ StringDecoder
組合 就是按行切換的文本解碼器,它被設計用來支持 TCP 的粘包和拆包。
除了 LineBasedFrameDecoder
以外,還有兩個常用的解碼器 DelimiterBasedFrameDecoder
和 FixedLengthFrameDecoder
,前者能自動對 “以分隔符做結束標志的消息” 進行解碼,后者可以自動完成對定長消息的解碼。使用方法也和前面的示例代碼相同,結合字符串解碼器 StringDecoder,輕松完成對很多消息的自動解碼。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。