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事件