一、代碼
@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());
就解決了~~~~~~~~
完結撒花~~~~~