日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

粘包拆包,Netty及远洋通信中的解决方案!超实用

發布時間:2025/3/16 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 粘包拆包,Netty及远洋通信中的解决方案!超实用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在進行Java NIO學習時,發現,如果客戶端連續不斷的向服務端發送數據包時,服務端接收的數據會出現兩個數據包粘在一起的情況,這就是TCP協議中經常會遇到的粘包以及拆包的問題。

一、什么粘包和拆包?

現在假設客戶端向服務端連續發送了兩個數據包,用packet1和packet2來表示,那么服務端收到的數據可以分為三種,現列舉如下:

1.接收端正常收到兩個數據包,即沒有發生拆包和粘包的現象,此種情況不在本文的討論范圍內。

2.接收端只收到一個數據包,由于TCP是不會出現丟包的,所以這一個數據包中包含了發送端發送的兩個數據包的信息,這種現象即為粘包。這種情況由于接收端不知道這兩個數據包的界限,所以對于接收端來說很難處理。

3.這種情況有兩種表現形式,如下圖。接收端收到了兩個數據包,但是這兩個數據包要么是不完整的,要么就是多出來一塊,這種情況即發生了拆包和粘包。這兩種情況如果不加特殊處理,對于接收端同樣是不好處理的。

TCP是個“流”協議,沒有界限的一串數據。TCP底層并不了解上層業務數據的具體含義,它會根據TCP緩沖區的實際情況進行包的劃分,所以在業務上認為,一個完整的包可能會被TCP拆分成多個包進行發送,也有可能把多個小的包封裝成一個大的數據包發送,這就是所謂的TCP粘包和拆包問題。

二、粘包、拆包發生的原因

  • 要發送的數據大于TCP發送緩沖區剩余空間大小,將會發生拆包。

  • 待發送數據大于MSS(最大報文長度),TCP在傳輸前將進行拆包。

  • 要發送的數據小于TCP發送緩沖區的大小,TCP將多次寫入緩沖區的數據一次發送出去,將會發生粘包。

  • 接收數據端的應用層沒有及時讀取接收緩沖區中的數據,將發生粘包。

  • 三、粘包、拆包解決辦法

    由于底層的TCP無法理解上層的業務數據,所以在底層是無法保證數據包不被拆分和重組的,這個問題只能通過上層的應用協議棧設計來解決,根據業界的主流協議的解決方案,歸納如下:

  • 消息定長。發送端將每個數據包封裝為固定長度(不夠的可以通過補0填充),這樣接收端每次接收緩沖區中讀取固定長度的數據就自然而然的把每個數據包拆分開來。

  • 設置消息邊界。服務端從網絡流中按消息邊界分離出消息內容。在包尾增加回車換行符進行分割,例如FTP協議。

  • 將消息分為消息頭和消息體,消息頭中包含表示消息總長度(或者消息體長度)的字段。

  • 更復雜的應用層協議,比如Netty中實現的一些協議都對粘包、拆包做了很好的處理。

  • 四、Netty的拆包解決方案

    Netty這個框架,對于客戶端和服務端之間的數據傳輸做了很好的處理,服務端在發送數據之前先對數據按一定的規則進行編碼,客戶端在接收到數據后按照相同的規則進行解碼,這就是Netty解決粘包拆包問題的思路,下面我們詳細來看一看。

    拆包這個工作,Netty 已經為大家備好了很多不同的拆包器。本著不重復發明輪子的原則,我們直接使用Netty現成的拆包器。

    Netty 中的拆包器大致如下:

    1.固定長度的拆包器 FixedLengthFrameDecoder

    每個應用層數據包的都拆分成都是固定長度的大小,比如 1024字節。

    對于使用固定長度的粘包和拆包場景,可以使用FixedLengthFrameDecoder,該解碼一器會每次讀取固定長度的消息,如果當前讀取到的消息不足指定長度,那么就會等待下一個消息到達后進行補足。其使用也比較簡單,只需要在構造函數中指定每個消息的長度即可。這里需要注意的是,FixedLengthFrameDecoder只是一個解碼一器,Netty也只提供了一個解碼一器,這是因為對于解碼是需要等待下一個包的進行補全的,代碼相對復雜,而對于編碼器,用戶可以自行編寫,因為編碼時只需要將不足指定長度的部分進行補全即可。

    數據在編碼發送的時候,也會以固定長度作為一調完整的消息。

    2.行拆包器 LineBasedFrameDecoder

    每個應用層數據包,都以換行符作為分隔符,進行分割拆分。

    數據在編碼發送的時候,會以換行符作為一條完整的消息。

    3.分隔符拆包器 DelimiterBasedFrameDecoder

    每個應用層數據包,都通過自定義的分隔符,進行分割拆分。這個版本,是LineBasedFrameDecoder 的通用版本,本質上是一樣的。

    數據在編碼發送的時候,會以一個自定義的分隔符作為一條完整的消息。

    4.基于數據包長度的拆包器 LengthFieldBasedFrameDecoder

    將應用層數據包的長度,作為接收端應用層數據包的拆分依據。按照應用層數據包的大小,拆包。這個拆包器,有一個要求,就是應用層協議中包含數據包的長度。

    LengthFieldBasedFrameDecoder與LengthFieldPrepender需要配合起來使用,其實本質上來講,這兩者一個是解碼,一個是編碼的關系。它們處理粘拆包的主要思想是在生成的數據包中添加一個長度字段,用于記錄當前數據包的長度。LengthFieldBasedFrameDecoder會按照參數指定的包長度偏移量數據對接收到的數據進行解碼,從而得到目標消息體數據;而LengthFieldPrepender則會在響應的數據前面添加指定的字節數據,這個字節數據中保存了當前消息體的整體字節數據長度。

    數據在編碼發送的時候,會指定當前這條消息的長度。

    五、實戰:遠揚通信中自定義協議的粘包、拆包解決方案

    對于我們要做的項目,也可以自己定義消息傳輸的協議,在我做過的一個項目中,遠洋貨輪需要進行通信,大家都知道,在海上信號是很差的,每次收消息都很難保證收到的是一條完整的消息,但此時我們可以自定義協議,在消息的頭部用兩個字節把本次消息發送的長度加上,中間部分是消息正文,消息的尾部用四個字節保存本條消息md5值的低四位。這樣,接收方在收到部分消息后,可根據消息的頭部判斷該條消息的具體長度,然后繼續接收消息,當收到完整的消息后,在去計算接收到消息的md5四位,去跟接收的低四位做比較,如果一致,就認為是收到了完整的消息,接著根據約定的協議進行解碼交流。

    當然,消息的校驗位還是比較復雜的,需要給每臺設備都指定唯一標識來區別身份等,這里就不展開敘述了。

    此種方案特別適合網絡環境差的情況,能保證正常的通信。也經受住了實戰的考驗,基本可以做到消息的零丟失。

    六、總結:一個面試題

    我們都知道TCP屬于傳輸層的協議,傳輸層除了有TCP協議外還有UDP協議。

    那么UDP是否會發生粘包或拆包的現象呢?

    答案是不會。UDP是基于報文發送的,從UDP的幀結構可以看出,在UDP首部采用了16bit來指示UDP數據報文的長度,因此在應用層能很好的將不同的數據報文區分開,從而避免粘包和拆包的問題。

    而TCP是基于字節流的,雖然應用層和TCP傳輸層之間的數據交互是大小不等的數據塊,但是TCP把這些數據塊僅僅看成一連串無結構的字節流,沒有邊界;另外從TCP的幀結構也可以看出,在TCP的首部沒有表示數據長度的字段,基于上面兩點,在使用TCP傳輸數據時,才有粘包或者拆包現象發生的可能。

    參考資料:

  • https://blog.csdn.net/ailunlee/article/details/95944377

  • https://blog.csdn.net/wxy941011/article/details/80428470

  • http://www.sohu.com/a/302231889_120045139

  • https://blog.csdn.net/crazymakercircle/article/details/83957259

  • 有道無術,術可成;有術無道,止于術

    歡迎大家關注Java之道公眾號

    好文章,我在看??

    總結

    以上是生活随笔為你收集整理的粘包拆包,Netty及远洋通信中的解决方案!超实用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。