netty 异常 did not read anything but decoded a message

翻译过来就是说:什么也没读,只是解码了一条信息
编码器是如下图

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

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值