Netty诡异报错did not read anything but decoded a message
###前言
用netty做数据校验的时候,很自然的想法是写一个decoder,比如XXXXChecksumDecoder,如果校验出错,就丢弃这个数据包,一般来说,这种单纯的做数据校验的decoder,不会改变数据大小,就是说,传入的bytebuf大小如果是10byte,传出的bytebuf大小也应该是10byte,decoder只是做了一次数据校验,这个时候,经常遇到的问题是netty报错:did not read anything but decoded a message,就是提示使用者,必须读取一些字节,传出的bytebuf必须比传入的bytebuf小。
###问题解决方法
一般来说,很自然的想法是把decoder写成这样:
public class XXXXChecksumDecoder extends ByteToMessageDecoder {@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {int checkSumRemote = in.readUnsignedShortLE(Header.CHECKSUM_POS);in.setShortLE(Header.CHECKSUM_POS, 0);int checkSumLocal = ChecksumUtil.calculateChecksumValue(in);in.setShortLE(Header.CHECKSUM_POS, checkSumRemote);if(checkSumLocal != checkSumRemote){in.skipBytes(in.readableBytes());System.out.println("CheckSum Error");}else {out.add(in);}}
}
但这样会抛出异常XXXXChecksumDecoder.decode()did not read anything but decoded a message
既然netty提示说必须要读走一些byte,那么这样行不行呢?
public class XXXXChecksumDecoder extends ByteToMessageDecoder {@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {int checkSumRemote = in.readUnsignedShortLE(Header.CHECKSUM_POS);in.setShortLE(Header.CHECKSUM_POS, 0);int checkSumLocal = ChecksumUtil.calculateChecksumValue(in);in.setShortLE(Header.CHECKSUM_POS, checkSumRemote);if(checkSumLocal != checkSumRemote){in.skipBytes(in.readableBytes());System.out.println("CheckSum Error");}else {in.skipBytes(in.readableBytes());//这里跳过所有可读字节out.add(in);}}
}
但是如果这样,上层decoder收到的bytebuf,就已经是被全部读过的了,就是bytebuf的可读区域已经归0了,那要怎么解决呢?正确的做法是这样:
public class XXXXChecksumDecoder extends ByteToMessageDecoder {@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {int checkSumRemote = in.readUnsignedShortLE(Header.CHECKSUM_POS);in.setShortLE(Header.CHECKSUM_POS, 0);int checkSumLocal = ChecksumUtil.calculateChecksumValue(in);in.setShortLE(Header.CHECKSUM_POS, checkSumRemote);if(checkSumLocal != checkSumRemote){System.out.println("CheckSum Error");}else {Bytebuf frame = in.retainedDuplicate();out.add(frame);}in.skipBytes(in.readableBytes());}
}
将in
的副本返回给上层decoder,并且跳过所有in
的可读字节。因为retainedDuplicate()
只是将in
的引用数加1并且复制其readerIndex、writerIndex等,并没有真的复制缓冲区,所以这样几乎不消耗额外性能。之后就安全地in.skipBytes(in.readableBytes())
读走所有字节。
###问题的原因
为什么会出现这样的问题呢?源码中是这样的:
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {try {while (in.isReadable()) {int outSize = out.size();int oldInputLength = in.readableBytes();decode(ctx, in, out);if (outSize == out.size()) {if (oldInputLength == in.readableBytes()) {break;} else {continue;}}if (oldInputLength == in.readableBytes()) {throw new DecoderException(StringUtil.simpleClassName(getClass()) +".decode() did not read anything but decoded a message.");}if (isSingleDecode()) {break;}}} catch (DecoderException e) {throw e;} catch (Throwable cause) {throw new DecoderException(cause);}
}
源码中,如果List<Object> out
不增长的话,是不会抛出这个异常的,比如定长数据包解析中decoder一开始检查到可读数据没有达到数据包的大小,直接return,这时候是不会报异常的,只有decoder在out
中增加了对象,就是说decoder产生了数据,但是传入的数据并没有减少,才会有这个错误,为什么要这样呢?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里读出一些字节,减少in的大小,如果不想读,像上面的checksum例子,那就必须复制一个in,然后把原来的in的数据读掉。
Netty诡异报错did not read anything but decoded a message相关推荐
- 【已解决】Springboot服务 Netty启动报错Failed to submit a listener
[已解决]Springboot服务 Netty启动报错Failed to submit a listener Force-closing a channel whose registration ta ...
- python request 报错 #No JSON object could be decoded
python request 报错 #No JSON object could be decoded Python 使用request 发起post请求报错如下 报错如下 解决方案如下 Python ...
- 一个DataFrame赋值的诡异报错 A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc
DataFrame赋值时报错 A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row ...
- Git三种报错:E325: ATTENTION、Please enter the commit message for your changes、Timed out
三种报错解决: 1.报错:E325: ATTENTION: 删除.git隐藏文件夹下的.COMMIT_EDITMSG.swp 2.报错:Please enter the commit message ...
- IDOC报错Ship-to party XXX can not be used, message no.VP204
当自动创建SO的IDOC出现如下报错时,该如何解决? 解决方法: 1.维护客户在对应的销售组织和分销渠道下的主数据. 2.BD87将报错的IDOC重新处理即可.
- 解决redis-server.exe闪退问题报错QForkMasterInit: system error caught. error code=0x000005af, message=Virtual
如果你有一天什么配置都没改,莫名其妙打开redis服务器闪退,可以在命令行中进入到redis-server的目录下并输入redis-server.exe redis.windows.conf命令查看报 ...
- idea创建类报错:Unable to parse template Class Error message: This template did not produc
在idea.exe.vmoptions 或 idea64.exe.vmoptions中加入配置 -Djdk.util.zip.ensureTrailingSlash=false 注意:字段尾部不要有空 ...
- Netty报错 远程主机强迫关闭了一个现有的连接 异常
百度百科的描述 Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客 ...
- 【Netty报错:】XXXDecoder.decode() did not read anything but decoded a message.
Netty解码器报错:XXXDecoder.decode() did not read anything but decoded a message. 从字面意思来看,就是说没有读取任何数据,但是却解 ...
最新文章
- AJPFX实列判断一个字符串是不是对称字符串
- linux性能测试 瓶颈,性能测试——瓶颈分析方法
- Android TimeAnimator
- c#格式化字float_C#中的float关键字
- HashMap 中的一个“坑”!
- 解决ora-00054 Oracle锁表问题
- 【Java数据结构】平衡二叉树
- 财务有必要学python吗-工作三年却被实习生抢了饭碗,学会Python到底有多吃香?...
- 软件工程综合实践第二次作业——结对编程
- 红手指云手机屏蔽方案
- [20151018]SCZ训练
- sql数据库去重语法_oracle大数据去重sql语句
- H3C设备忘记密码修改办法
- android声音大小锁定,固定音量锁(锁定音量)app
- CAD将文字变成曲线(网页版)
- 人工智能轨道交通行业周刊-第25期(2022.11.28-12.4)
- 如何计算Java对象的大小
- 48万的无人共享车,能让百度破局?还是能“拯救”极狐?
- 扳倒井酒病毒性营销方案策划
- docker笔记之部署安装