編碼器與解碼器

編碼器與解碼器


以ProtobufEncoder爲例子,
ProtobufEncoder
ProtobufEncoder繼承ChannelOutboundHandler,與我們自定義的出站處理器(OutboundHandler)相似。
出站時(即向Channel寫數據),會調用這個Handler對數據進行處理(編碼)。

同理,Decoder則繼承ChannelInboundHandler,是一個入站處理器,當向Channel讀數據時,會調用這個handler對數據進行處理,再將已經解碼的數據傳遞給下一個handler。(這就是爲什麼要把自定義處理器放在最末尾)

Handler鏈


出站從Handler鏈的尾部執行到頭部,而入站從頭部執行到尾部。

如果發送的數據與預期的不同,那麼編碼器就不會處理。

public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Long msg, ByteBuf out) throws Exception {
        System.out.println("Long to Byte 被調用");
        out.writeLong(msg);
    }
}

如上是一個自定義的編碼器,當寫出類型不是泛型中指定的類型,那麼編碼器就不會執行。

			if (acceptOutboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                I cast = (I) msg;
                buf = allocateBuffer(ctx, cast, preferDirect);
                try {
                    encode(ctx, cast, buf);
                } finally {
                    ReferenceCountUtil.release(cast);
                }

                if (buf.isReadable()) {
                    ctx.write(buf, promise);
                } else {
                    buf.release();
                    ctx.write(Unpooled.EMPTY_BUFFER, promise);
                }
                buf = null;
            } else {
                ctx.write(msg, promise);
            }

如上是io.netty.handler.codec.MessageToByteEncoder#write方法,如果不是指定的類型,就直接寫出而不進行編碼。

如果客戶端發送的信息字節大於8個字節(Long類型的長度),那麼解碼器以及自定義的ChannelRead方法會執行多次。

如;

服務器端的解碼器

public class MyByteToLongDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("Decoder被調用");
        if (in.readableBytes() >= 8){
            // 一個long是8個字節,所以每次傳輸必是8的倍數
            // 每執行一次readLong,喫掉8個字節
            out.add(in.readLong());
        }
    }
}

服務器端的ChannelRead:

	@Override
    protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {
        System.out.println("Server接收到數據");
        System.out.println(msg);
    }

如果發送的是"abcdabcdabcdabcd",那麼解碼器和channelRead0就會執行兩次。

  • 接收的解碼器和發出的編碼器處理的消息類型要一致,否則此handler不會執行
  • 在解碼器進行解碼時,需要判斷緩存區的數據量是否足夠,如果不足可能會與期望的結果不一致。
    如下
	@Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("Decoder被調用");
        // 有8個字節以上的數據量纔會讀取
        if (in.readableBytes() >= 8){
            // 一個long是8個字節,所以每次傳輸必是8的倍數
            // 每執行一次readLong,喫掉8個字節
            out.add(in.readLong());
        }
    }

其他解碼器


  • ReplayingDecoder
    ReplayingDecoder< S >,通過這個類,我們可以不用調用readableBytes(),參數S指定了用戶狀態的管理的類型,Void代表不需要狀態管理。
  • LineBasedFrameDecoder:使用\n\r\作爲分隔符來解析數據。
  • DelimitBasedFrameDecoder:自定義的特殊字符作爲消息的分隔符
  • HttpObjectDecoder:HTTP數據的解碼器
  • LengthFieldBaseFrameDecoder:指定長度來標識整包消息,這樣就可以自動處理黏包和半包消息。
  • ZlibEncoder:壓縮數據
  • ZlibDecoder:解壓縮數據
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章