解碼器 與 編碼器

解碼器

ByteToMessageDecoder 抽象類

將字節解碼爲消息(或者另一個字節序列), Netty 爲它提供了一個抽象的基類: ByteToMessageDecoder.

由於你不可能知道遠程節點是否會一次性地發送一個完整的消息, 所以這個類會對入站數據進行緩衝, 直到它準備好處理. 只是將消息進行緩衝, 並不會進行解碼操作. 如何緩衝的下面會說.

下面這張圖說明了在網絡傳輸中可能出現的情況.

clipboard.png

ByteToMessageDecoder 抽象類有兩個重要方法.

方 法 描 述
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 狀態爲非活動時, 這個方法會被調用一次. 可以重寫該方法已提供特殊處理.

ByteToMessageDecoder 的屬性:

clipboard.png

cumulation屬性: 用來保存累計讀取到的字節. 我們讀到的新字節會保存(緩衝)在這裏.
cumulator屬性: 用來做累計的. 負責將讀到的新字節寫入 cumulation. 有兩個實現 MERGE_CUMULATORCOMPOSITE_CUMULATOR.
singleDecode: 設置爲true後, 單個解碼器只會解碼出一個結果.
decodeWasNull: 解碼結果爲空.
first: 是否是第一次讀取數據.
discardAfterReads: 多少次讀取後, 丟棄數據 默認16次.
numReads: 已經累加了多少次數據了.

重點

我們實現 ByteToMessageDecoder 接口時, 最主要的方法就是 decode, 當有新數據進入時, 會先緩衝數據然後將緩衝後的數據傳遞給我們.

我們進行解碼時, 當解碼成功後我們將數據放入 decode 方法中的, List<Object> out 集合中, 這樣就會傳遞給下個 ChannelInboundHandler. 如果解碼失敗就不用操作 List<Object> out 集合.

就向 io.netty.handler.codec.LineBasedFrameDecoder 實現類中的一樣

    @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) 方法. 這將會增加該引用計數, 從而防止該消息被釋放.

MessageToMessageDecoder 抽象類

public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter

ByteToMessageDecoder 抽象類一樣, 最主要的還是 decode 方法.

decode (ChannelHandlerContext ctx, I msg, List<Object> out)

只不過會將接到的消息強制類型轉換爲 I, 而且不會對消息進行緩衝.

TooLongFrameException 類

由於 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());
        }
    }
}

編碼器

MessageToByteEncoder 抽象類

只有一個必須要實現的方法.

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中
    }
}

MessageToMessageEncoder 抽象類

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));
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章