將字節解碼爲消息(或者另外一個字節序列), Netty 爲它提供了一個抽象的基類: ByteToMessageDecoder
. 網絡
因爲你不可能知道遠程節點是否會一次性地發送一個完整的消息, 因此這個類會對入站數據進行緩衝, 直到它準備好處理. 只是將消息進行緩衝, 並不會進行解碼操做. 如何緩衝的下面會說.框架
下面這張圖說明了在網絡傳輸中可能出現的狀況.異步
ByteToMessageDecoder
抽象類有兩個重要方法.ide
方 法 | 描 述 |
---|---|
decode(ChannelHandlerContext ctx,ByteBuf in,List<Object> out) | 必須實現的惟一抽象方法. 方法被調用時傳入一個包含新數據的ByteBuf, 和一個添加解碼消息的List. 對方法的調用會重複進行, 直到沒有新元素被添加到List, 或ByteBuf中沒有更多可讀取的字節. 若是List不爲空, 它的內容會被傳遞給 ChannelPipeline 中的下一個 ChannelInboundHandler . |
decodeLast(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) | 簡單調用 decode() 方法, 當 Channel 狀態爲非活動時, 這個方法會被調用一次. 能夠重寫該方法已提供特殊處理. |
cumulation屬性: 用來保存累計讀取到的字節. 咱們讀到的新字節會保存(緩衝)在這裏.
cumulator屬性: 用來作累計的. 負責將讀到的新字節寫入 cumulation. 有兩個實現 MERGE_CUMULATOR
和 COMPOSITE_CUMULATOR
.
singleDecode: 設置爲true後, 單個解碼器只會解碼出一個結果.
decodeWasNull: 解碼結果爲空.
first: 是不是第一次讀取數據.
discardAfterReads: 多少次讀取後, 丟棄數據 默認16次.
numReads: 已經累加了多少次數據了.編碼
咱們實現 ByteToMessageDecoder
接口時, 最主要的方法就是 decode
, 當有新數據進入時, 會先緩衝數據而後將緩衝後的數據傳遞給咱們. spa
咱們進行解碼時, 當解碼成功後咱們將數據放入 decode
方法中的, List<Object> out
集合中, 這樣就會傳遞給下個 ChannelInboundHandler
. 若是解碼失敗就不用操做 List<Object> out
集合..net
就向 io.netty.handler.codec.LineBasedFrameDecoder
實現類中的同樣netty
@Override protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { Object decoded = decode(ctx, in); if (decoded != null) { out.add(decoded); } }
引用計數須要特別的注意. 對於編碼器和解碼器來講, 其過程也是至關的簡單: 一旦消息被編碼或者解碼, 它就會被 ReferenceCountUtil.release(message)
調用自動釋放. 若是你須要保留引用以便稍後使用, 那麼你能夠調用 ReferenceCountUtil.retain(message)
方法. 這將會增長該引用計數, 從而防止該消息被釋放.code
public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter
與 ByteToMessageDecoder
抽象類同樣, 最主要的仍是 decode
方法.接口
decode (ChannelHandlerContext ctx, I msg, List<Object> out)
只不過會將接到的消息強制類型轉換爲 I
, 並且不會對消息進行緩衝.
因爲 Netty 是一個異步框架, 因此須要在字節能夠解碼以前在內存中緩衝它們. 所以, 不能讓解碼器緩衝大量的數據以致於耗盡可用的內存. 爲了解除這個常見的顧慮, Netty 提供了 TooLongFrameException
類, 其將由解碼器在幀超出指定的大小限制時拋出.
爲了不這種狀況, 你能夠設置一個最大字節數的閾值, 若是超出該閾值, 則會致使拋出一個 TooLongFrameException
(隨後會被 ChannelHandler.exceptionCaught()
方法捕獲). 而後, 如何處理該異常則徹底取決於該解碼器的用戶. 某些協議 (如HTTP) 可能容許你返回一個特殊的響應. 而在其餘的狀況下, 惟一的選擇可能就是關閉對應的鏈接.
public class ToIntegerDecoder extends ByteToMessageDecoder { private static final int MAX_FRAME_SIZE= 1024; @Override public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { int readable = in.readableBytes(); if (readable > MAX_FRAME_SIZE)//檢查緩衝區中是否有超過MAX_FRAME_SIZE個字節 { in.skipBytes(readable);//130第10章 編解碼器框架將Integer消息轉換爲它的String表示,並將其添加到輸出的List中跳過全部的可讀字節,拋出TooLongFrame-Exception並通知ChannelHandler throw new TooLongFrameException("Frametoo big!"); } if (readable >= 4) { out.add(in.readInt()); } } }
只有一個必需要實現的方法.
encode(ChannelHandlerContext ctx, I msg, ByteBuf out)
encode()
方法是你須要實現的惟一抽象方法. 它被調用時將會傳入要被該類編碼爲 ByteBuf
的 (類型爲I的) 出站消息. 該 ByteBuf 隨後將會被轉發給 ChannelPipeline
中的下一個 ChannelOutboundHandler
.
這個類只有一個方法, 而解碼器有兩個. 緣由是解碼器一般須要在 Channel 關閉以後產生最後一個消息 (所以也就有了decodeLast()方法). 這顯然不適用於編碼器的場景——在鏈接被關閉以後仍然產生一個消息是毫無心義的.
示例, ShortToByteEncoder
, 其接受一個 Short 類型的實例做爲消息, 將它編碼爲 Short 的原子類型值, 並將它寫入 ByteBuf 中, 其將隨後被轉發給 ChannelPipeline
中的下一個 ChannelOutboundHandler
. 每一個傳出的 Short 值都將會佔用 ByteBuf
中的2字節.
public class ShortToByteEncoder extends MessageToByteEncoder<Short> { @Override public void encode(ChannelHandlerContext ctx, Short msg, ByteBuf out) throws Exception { out.writeShort(msg);//將Short寫入ByteBuf中 } }
encode(ChannelHandlerContext ctx,I msg,List<Object> out)
這是你須要實現的惟一方法. 每一個經過 write()
方法寫入的消息都將會被傳遞給 encode()
方法, 以編碼爲一個或者多個出站消息. 隨後, 這些出站消息將會被轉發給 ChannelPipeline
中的下一個 ChannelOutboundHandler
.
示例, 編碼器將每一個出站 Integer 的 String 表示添加到了該 List 中.
public class IntegerToStringEncoder extends MessageToMessageEncoder<Integer> { @Override public void encode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception { out.add(String.valueOf(msg)); } }