###前言
用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相关推荐

  1. 【已解决】Springboot服务 Netty启动报错Failed to submit a listener

    [已解决]Springboot服务 Netty启动报错Failed to submit a listener Force-closing a channel whose registration ta ...

  2. python request 报错 #No JSON object could be decoded

    python request 报错 #No JSON object could be decoded Python 使用request 发起post请求报错如下 报错如下 解决方案如下 Python ...

  3. 一个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 ...

  4. 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 ...

  5. IDOC报错Ship-to party XXX can not be used, message no.VP204

    当自动创建SO的IDOC出现如下报错时,该如何解决? 解决方法: 1.维护客户在对应的销售组织和分销渠道下的主数据. 2.BD87将报错的IDOC重新处理即可.

  6. 解决redis-server.exe闪退问题报错QForkMasterInit: system error caught. error code=0x000005af, message=Virtual

    如果你有一天什么配置都没改,莫名其妙打开redis服务器闪退,可以在命令行中进入到redis-server的目录下并输入redis-server.exe redis.windows.conf命令查看报 ...

  7. idea创建类报错:Unable to parse template Class Error message: This template did not produc

    在idea.exe.vmoptions 或 idea64.exe.vmoptions中加入配置 -Djdk.util.zip.ensureTrailingSlash=false 注意:字段尾部不要有空 ...

  8. Netty报错 远程主机强迫关闭了一个现有的连接 异常

    百度百科的描述 Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客 ...

  9. 【Netty报错:】XXXDecoder.decode() did not read anything but decoded a message.

    Netty解码器报错:XXXDecoder.decode() did not read anything but decoded a message. 从字面意思来看,就是说没有读取任何数据,但是却解 ...

最新文章

  1. AJPFX实列判断一个字符串是不是对称字符串
  2. linux性能测试 瓶颈,性能测试——瓶颈分析方法
  3. Android TimeAnimator
  4. c#格式化字float_C#中的float关键字
  5. HashMap 中的一个“坑”!
  6. 解决ora-00054 Oracle锁表问题
  7. 【Java数据结构】平衡二叉树
  8. 财务有必要学python吗-工作三年却被实习生抢了饭碗,学会Python到底有多吃香?...
  9. 软件工程综合实践第二次作业——结对编程
  10. 红手指云手机屏蔽方案
  11. [20151018]SCZ训练
  12. sql数据库去重语法_oracle大数据去重sql语句
  13. H3C设备忘记密码修改办法
  14. android声音大小锁定,固定音量锁(锁定音量)app
  15. CAD将文字变成曲线(网页版)
  16. 人工智能轨道交通行业周刊-第25期(2022.11.28-12.4)
  17. 如何计算Java对象的大小
  18. 48万的无人共享车,能让百度破局?还是能“拯救”极狐?
  19. 扳倒井酒病毒性营销方案策划
  20. docker笔记之部署安装

热门文章

  1. 国产芯片传来好消息,纯国产CPU测试数据“曝光”
  2. 神经网络传递函数的选择,神经网络的函数表达式
  3. 【客户是瞎子】腾讯微博注册的不友好提示
  4. 如何设置计算机的休眠时间,电脑的睡眠时间如何设置?
  5. 用什么工具可以批量查询韵达快递
  6. 针对正方教务开发大学App(查成绩,课表,一键评教,图书馆,正方系统)
  7. 测试测开面试要知道的那些事02
  8. 再携手,齐并进!菊风助力宁波银行坐席PUSH外呼项目
  9. 微信分享与支付开发详解
  10. 遥控汽车网页小游戏源码