【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());

就解決了~~~~~~~~

完結撒花~~~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章