Netty 粘包與拆包

粘包 拆包 原理淺析 Netty中的應用 

2016年拍攝於臺灣省日月潭制高點慈恩塔,只有這個地方纔能纔看什麼是日哪個是月。

  微信公衆號

王皓的GitHub:https://github.com/TenaciousDWang

 

        今天來說一下網絡通信過程中一些不可避免的數據包粘包與半包問題,應該如何解決。

 

        雖然我們在應用層Netty中都是已一個完整的數據包編碼發送的,但是到了系統底層及網絡層仍然是以字節流發送數據的,數據到了另一端仍然是按照字節流讀取的,這個時候會存在兩種錯誤的情況。

 

        一種是我們發出的多個數據包轉爲字節流後,被另一端讀取到的字節流可能爲多個數據包粘在一起的字節數據,經過解碼後,可能出現多條信息或者錯誤,另一種是發出的一個數據包的字節流。

        

        另一端由於網絡原因只讀取到一半,在解碼過程中,可能只讀取到一半信息或者報錯。也就是說通信兩端不能保證發出與接收的字節數是對等的,可能存在偏差和延時。

 

        因此我們需要定義一套規則,讓客戶端按照規則組裝數據包,我們稱之爲粘包,讓服務端按照規則來讀取數據包,我們稱之爲拆包,粘包與拆包是相對的,例如,服務端將三個數據包拼成兩個TCP包發送出去,服務端則將兩個TCP包拆開後重新組裝爲三個數據包,這個過程就是粘包與拆包的過程。

 

        通常我們無法控制數據在網絡中的傳輸過程,只能在一端讀取時想辦法,這裏我們就來說一下如何來拆包,一般拆包,需要做一個緩衝區域,不斷的讀取字節數據到緩衝區,然後每次都要從緩衝區中讀取數據包,直到讀取出一個完整的數據包,不完整的片段還是要繼續放在緩衝區,等到完整後再讀取。如果我們手動拆包,會很麻煩,在這裏Netty爲我們提供了開箱即用的拆包神器,接下來我們來看一下。

 

1. 固定長度的拆包器 FixedLengthFrameDecoder

2. 行拆包器 LineBasedFrameDecoder

3. 分隔符拆包器 DelimiterBasedFrameDecoder

4. 基於長度域拆包器 LengthFieldBasedFrameDecoder

        基於長度域拆包器最通用的一種拆包器,這裏我們來說一下,其他的拆包器官方文檔中已經列舉了很多例子這裏不再描述。

 

    new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 74);

 

       LengthFieldBasedFrameDecoder類構造函數有多個重載,這裏我們只根據當前通信協議說一種最常用的,第一個參數爲數據包最大長度,第二個參數爲數據長度域的偏移量,也就是數據長度所在數據包內的開始位置,第三個參數爲數據長度域的長度,也就是我們用來描述數據長度的空間爲4個字節。

 

      

        根據我們當前的通信協議。魔數4個字節,版本號1個字節,序列化算法標識1個字節,指令1個字節,那麼我們可以算出長度域偏移量爲4+1+1+1=7。長度域長度爲4,最大數據包長度我們用Integer.MAX_VALUE來表示,最大爲2GB。

 

 

        之前我們提到過一個魔數的概念,我們可以在拆包時,同時判斷該數據包是否是我們支持的協議,如果不是我們可以直接關閉連接,以節省資源。

 

 

  我們創建一個邏輯處理器Spliter,繼承LengthFieldBasedFrameDecoder然後在構造器中創建父類,這樣就可以獲得一個完整的數據包,然後覆寫父類decode方法,這個方法每次都會傳入一個完整的ByteBuf,我們讀取頭4個字節獲得魔數,如果不是支持的協議,直接關閉連接,如果支持直接返回調用decode的結果,即一個完整的ByteBuf數據包。

 

 

        到此我們的客戶端與服務端的pipeline結構爲下圖。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章