編碼器與解碼器
以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:解壓縮數據