翻译过来就是说:什么也没读,只是解码了一条信息
编码器是如下图
public class ByteToIntegerDecoder extends ByteToMessageDecoder {
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in,
List<Object> out) throws Exception {
//ByteBuf byteBuf = in.retainedDuplicate();
ByteBuf byteBuf=in;
int readerIndex = byteBuf.readerIndex();
int i = byteBuf.readableBytes();
if (i >= 4) { // Check if there are at least 4 bytes readable
String str;
if(byteBuf.hasArray()) { // 处理堆缓冲区
str = new String(byteBuf.array(), byteBuf.arrayOffset() + byteBuf.readerIndex(), byteBuf.readableBytes());
} else { // 处理直接缓冲区以及复合缓冲区
byte[] bytes = new byte[in.readableBytes()];
byteBuf.getBytes(readerIndex, bytes);
str = new String(bytes, 0, byteBuf.readableBytes());
}
System.err.println("ByteToIntegerDecoder decode msg is " + str);
out.add(str); //Read integer from inbound ByteBuf, add to the List of decodec messages
}
}
}
我传过来是个String字符串,当做对象来解码
系统显示打印出了我发过来的字符串,然后又执行了一遍就报错了。
nett资料上如下图:
大概意思就是说,这个ByteBuf还是是netty的那个传数据流的容器,这个List 是最终要返回给下面handler用的东西
传数据时这个decode方法会一直被调用,直到ByteBuf里面没有可以读取的数据,并且List也不是null就会传给下面的handler了。
那重点就是这俩:1.直到ByteBuf里面没有可以读取的数据 2.并且List也不是null
符合这俩条件,decode就会把数据交给handler。
2.问题我好理解,我out.add了,List已经不是null了。
1.问题就是怎么知道ByteBuf里有没有数据了?
那得看ByteBuf咋工作了:
代码里,先bytebuf读取一下里面可以读取的字节容量,然后判断了是否大于4,如果大于4就表示我可以进行解析然后往List里添加了数据了。
然后通过String的构造方法把byte数组转换成String字符串,起始是0,截止是Bytebuf里放的流数据长度。
通过打断点发现,这个方法会在数据刚进入解码是调用一次,读取到可读长度,赋值给oldInputLength;之后会在最后结束此次解码时候再进去一次,if (outSize == out.size()) 这就是判断有没往out里加入数据,初始 outSize = 0;if (oldInputLength == in.readableBytes()) 这个是判断你有没有去把ByteBuf in里的东西给读出来。如果还是和之前赋值的oldInputLength一样的大小,那就表明没有读取过。这样就会抛出以上的异常。
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
try {
while(true) {
if (in.isReadable()) {
int outSize = out.size();
if (outSize > 0) {
fireChannelRead(ctx, out, outSize);
out.clear();
if (ctx.isRemoved()) {
return;
}
outSize = 0;
}
int oldInputLength = in.readableBytes();
this.decodeRemovalReentryProtection(ctx, in, out);
if (!ctx.isRemoved()) {
if (outSize == out.size()) {
if (oldInputLength != in.readableBytes()) {
continue;
}
} else {
if (oldInputLength == in.readableBytes()) {
throw new DecoderException(StringUtil.simpleClassName(this.getClass()) + ".decode() did not read anything but decoded a message.");
}
if (!this.isSingleDecode()) {
continue;
}
}
}
}
return;
}
} catch (DecoderException var6) {
throw var6;
} catch (Exception var7) {
throw new DecoderException(var7);
}
}
结合Buf的原理,readindex如果不处理就会每次读取重复的数据,所以官方加判断并抛异常解决了这个死循环问题。
所以最常见的办法就是加上:skipBytes方法,将readindex往前移动数个位置
如:byteBuf.skipBytes(byteBuf.readableBytes());
下次直接从移动后的位置开始往后读取,就不会造成重复读取数据了
网上有人说要备份一下,否则影响其他decoder,这个还不理解为什么,这里mark一下
大概意思就是说,这个decoder把readindex往前移动了,但是其他decoder不能被影响,也要从头解析一遍,是这个意思吧
https://blog.csdn.net/zougen/article/details/79047252?utm_source=blogxgwz0