【Netty - 解码器】did not read anything but decoded a message 异常

一、代码

@Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
        if(in.readableBytes() <= 0){
            log.info("***********当前无数据报文,无需解码***********");
            return;
        }

        SocketData data = new SocketData(in);
        data.setpId(this.pId);
        ChannelData channelData =  new ChannelData(data);
        ReferenceCountUtil.retain(in);
        out.add(channelData);
    }

二、现象就是抛出如题的错误。打断点调试,在无数据的情况下是不会抛出异常,但是只要是往out里面放入过数据,就会抛出如下异常。

2020-04-06 17:07:23 [nioEventLoopGroup-5-1] ERROR c.q.qxzniotgw.client.handle.Client4TerminalHandler - Exception:{}
io.netty.handler.codec.DecoderException: GateTCP4TerminalDecoderMulti.decode() did not read anything but decoded a message.
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:448)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:647)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:582)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:499)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:461)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.lang.Thread.run(Thread.java:748)

三、抛出异常的netty源码

通过打断点发现,这个方法会在数据刚进入解码是调用一次,读取到可读长度,赋值给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);
        }
    }

四、借鉴的博客:https://blog.csdn.net/zougen/article/details/79047252?utm_source=blogxgwz0

源码中,如果List<Object> out不增长的话,是不会抛出这个异常的,比如定长数据包解析中decoder一开始检查到可读数据没有达到数据包的大小,直接return,这时候是不会报异常的,只有decoder在out中增加了对象,就是说decoder产生了数据,但是却没有读in时,才会有这个错误,为什么要这样呢?Netty的作者给出了答案:

if you produce a message you need to also read something from the ByteBuf. This check was added to catch endless loops generated by user decoder bugs.

这是用来防止由decoder引起的无限循环的机制,这么想,如果每次decoder都生成一个新对象,但是in的readerIndex却不增长,这样再次调用decoder时,传入的in的readerIndex还是一样的,这时候decoder又会生成一个新对象,虽然不是一定的,但是这样容易引起无限循环,所以netty用异常来警告使用者,每次都必须从in里读出一些字节,如果不想读,像上面的checksum例子,那就必须复制一个in,然后把原来的in的数据读掉。

五、最后的解决方法

综上的原因就是netty 的 解码 鸡儿 在out有数据的情况不让不读呗  so,我只要在后面加上

in.skipBytes(in.readableBytes());

就解决了~~~~~~~~

完结撒花~~~~~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值