Netty 源碼分析之ByteToMessageDecoder

ByteToMessageDecoder是netty中的一個ChannelHandler,用於將ByteBuf轉換成Message,message可以是POJO等等,轉換後繼續在ChannelPipeline中傳遞,Decoder和Encoder等設置顯示了netty的ChannelPipeline帶來的強大的靈活性,並且可以使我們複用很多邏輯代駕,分離職責。

ByteToMessageDecoder繼承自ChannelHandlerAdapter,需要開發者實現的是decode方法

protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;

ctx是decode屬於的ChannelHandlerContext,in是用來讀取的數據,out是我們轉換後的對象列表
一個將Byte轉換成Integer的例子

class ByteToIntegerDecoder extends ByteToMessageDecoder{
        @Override
        protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
            while(in.readableBytes() > 4){
                out.add(in.readInt());
            }
        }
    }

分析源碼中較爲關鍵的channelRead

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {
            RecyclableArrayList out = RecyclableArrayList.newInstance();
            try {
                ByteBuf data = (ByteBuf) msg;
                first = cumulation == null;
                if (first) {
                    cumulation = data;
                } else {
                    cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
                }
                callDecode(ctx, cumulation, out);
            } catch (DecoderException e) {
                throw e;
            } catch (Throwable t) {
                throw new DecoderException(t);
            } finally {
                if (cumulation != null && !cumulation.isReadable()) {
                    cumulation.release();
                    cumulation = null;
                }
                int size = out.size();

                for (int i = 0; i < size; i ++) {
                    ctx.fireChannelRead(out.get(i));
                }
                out.recycle();
            }
        } else {
            ctx.fireChannelRead(msg);
        }
    }

cumulation是代表累加的byte數據,即上一次decode剩下的byte,cumulator是累加器,默認使用MERGE_CUMULATOR就是使用內存複製來進行累加。累加完之後調用callDecode(ctx, cumulation, out),callDecode中循環調用我們要實現的抽象方法decode(ctx,in,out) 來解碼,知道不能繼續解。
在finally中對out列表中的每一個對象調用ctx.fireChannelRead(out.get(i))
觸發ChannelPipeline後面的ChannelHandler的channelRead事件

發佈了43 篇原創文章 · 獲贊 28 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章