Netty 粘包 & 拆包 & 編碼 & 解碼 & 序列化 介紹

目錄:設計模式

  1. 粘包 & 拆包及解決方案 ByteToMessageDecoder
  2. 基於長度編解碼器
  3. 基於分割符的編解碼器
  4. google 的 Protobuf 序列化介紹
  5. 其餘的

 前言

Netty 做爲一個網絡框架,對 TCP 鏈接中的問題都作了全面的考慮,好比粘包拆包致使的半包問題,如何編解碼,如何實現私有協議,序列化等等。本文主要針對這些問題作一個簡單介紹,目的是想對整個 Netty 的編解碼框架作一個全盤的審視,以確保在後面的源碼學習中不會一葉障目不見泰山。緩存

1. 粘包 & 拆包及解決方案 ByteToMessageDecoder

因爲TCP是面向字節流的,什麼意思呢:雖然應用程序和 TCP 的交互是一次一個數據塊(大小不等),但 TCP 把應用程序交下來的數據僅僅當作式一連串的無結構的字節流。TCP 並不知道所傳送的字節流的含義。網絡

所以 TCP 不保證接收方應用程序所收到的數據塊和發送方應用程序所發出的數據塊具備對應大小的關係(例如,發送方應用程序交給發送方的 TCP 共 10 個數據塊,但接收方的 TCP 可能只用了 4 個就把收到的字節流交付上層的應用程序)。框架

同時,TCP 不關心應用進程一次把多長的報文發送到 TCP 的 緩存 中,而是根據對方給出的窗口值和當前網絡阻塞的程度來決定一個報文段應包含多少個字節(UDP 發送的報文長度是應用進程給出的)。若是應用進程傳送到 TCP 緩存的數據塊太長,TCP 就能夠把他劃分短一點再傳送。若是應用程序一次只發來一個字節,TCP 也能夠等待積累有足夠多的字節後再構成報文段發送出去。異步

  • TCP 發送報文通常是 3 個時機:
  1. 緩衝區數據達到 最大報文長度 MSS
  2. 由發送端的應用進程指明要求發送報文段,即 TCP 支持的推送(push)操做;
  3. 當發送方的一個計時器期限到了,即便長度不超過 MSS ,也發送。

以上引自《計算機網絡-----謝希仁》。函數

說了這麼多,TCP 的這種機制,會致使什麼問題呢?粘包問題。有了粘包,就須要拆包。工具

  • 通常解決粘包拆包問題有 4 種辦法:
  1. 固定數據的長度,好比 100 字節,若是不夠就補空格。
  2. 學習 HTTP ,FTP 等,使用回車換行符號。
  3. 將消息分爲 head 和 body,head 中包含 body 長度的字段,通常 head 的第一個字段使用 int 值來表示 body 長度。
  4. 使用更復雜的應用層協議(等於沒說 =_= !)。

Netty 做爲一個網絡框架,直接和 TCP 打交道,天然考慮了這個問題。而解決這個問題的主要實現就是抽象類 ByteToMessageDecoder,詳見 Netty 解碼器抽象父類 ByteToMessageDecoder 源碼解析。Netty 使用了模板設計模式,這個類只定義了共有行爲,具體解碼實現仍是子類,好比上面提到的 4 種方式。性能

2. 基於長度編解碼器的具體實現

基於長度的實現有2個現成的類:學習

  1. FixedLengthFrameDecoder 基於構造函數中的固定長度
    該類很簡單,構造方法中,傳入一個整數,該解碼器就會按照這個數字對累積區的字節進行切分。google

  2. LengthFieldBasedFrameDecoder 基於流中動態的長度
    該類比較複雜。構造函數參數多達 6 個,在構建私有協議棧時大有用處。

3. 基於分割符的編解碼器

一樣有 2 個:

  1. DelimiterBasedFrameDecoder 用戶提供分割符。
    該類比較簡單,根據用戶提供的分割符對累積區的內容進行分割。性能相對不是那麼完美。

  2. LineBasedFrameDecoder 基於換行符,支持多種換行符 \n \r\n 速度相比自定義較快。
    該類使用更簡單,根據換行符進行拆包粘包。

4. google 的 ProtobufDecoder ProtobufEncoder 序列化介紹

Netty 中有不少序列化工具,好比 Jboss 的 Marshalling,同時也支持 Java 標準的序列化。 但咱們重點關注 google 的 protobuf 庫。由於它的性能最高。

上面的 4 個解碼器都是基於 ByteToMessageDecoder,將粘包的字節轉爲用戶須要的字節。而ProtobufDecoder 不是繼承自 ByteToMessageDecoder,而是繼承自 MessageToMessageDecoder,名字都不一樣。MessageToMessageDecoder 的做用是什麼呢?

從名字上看,該類用於將兩個消息進行轉換(好比一種 POJO 轉成另外一種)。後面咱們將花大篇幅講述這個類庫。

5. 其餘的

1. TooLongFrameException

因爲 Netty 是一個異步框架,因此須要在字節能夠解碼以前在內存中緩衝他們。所以不能讓解碼器緩衝大量的數據以致於耗盡可用的內存。爲了解決這個問題,Netty 提供了 TooLongFrameException 類,其將由解碼器在幀超出指定的大小限制時拋出異常。

你能夠設置一個最大的閾值,當超過該閾值,這拋出異常。

2. 寫大型數據的 FileRegion

有時候你可能須要寫一個大型的數據,若是不停的寫入,可能致使 OOM,因此在寫大型數據時,須要準備好處理到遠程節點的鏈接時慢速鏈接的狀況,這種狀況會致使內存釋放的延遲。

咱們可使用 NIO 的零拷貝特性,這種特性消除了將文件內容從文件系統移動到網絡棧的複製過程。而咱們所須要作的就是使用一個 FileRegion 接口的實現。
官方定義:

經過支持零拷貝的文件傳輸的 Channel 來發送的文件區域。

6. 總結

本文並無刨析源碼,主要是針對 Netty 中現有的或者設計的編解碼,序列化等工具作一個介紹,方便後面有條不紊的按照這個路線研究他們的具體實現。

good luck!!!!

相關文章
相關標籤/搜索