一、代码
@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());
就解决了~~~~~~~~
完结撒花~~~~~