netty 异常 did not read anything but decoded a message

矫情吗;* 2023-02-27 10:54 70阅读 0赞

翻译过来就是说:什么也没读,只是解码了一条信息
编码器是如下图

  1. public class ByteToIntegerDecoder extends ByteToMessageDecoder {
  2. @Override
  3. public void decode(ChannelHandlerContext ctx, ByteBuf in,
  4. List<Object> out) throws Exception {
  5. //ByteBuf byteBuf = in.retainedDuplicate();
  6. ByteBuf byteBuf=in;
  7. int readerIndex = byteBuf.readerIndex();
  8. int i = byteBuf.readableBytes();
  9. if (i >= 4) { // Check if there are at least 4 bytes readable
  10. String str;
  11. if(byteBuf.hasArray()) { // 处理堆缓冲区
  12. str = new String(byteBuf.array(), byteBuf.arrayOffset() + byteBuf.readerIndex(), byteBuf.readableBytes());
  13. } else { // 处理直接缓冲区以及复合缓冲区
  14. byte[] bytes = new byte[in.readableBytes()];
  15. byteBuf.getBytes(readerIndex, bytes);
  16. str = new String(bytes, 0, byteBuf.readableBytes());
  17. }
  18. System.err.println("ByteToIntegerDecoder decode msg is " + str);
  19. out.add(str); //Read integer from inbound ByteBuf, add to the List of decodec messages
  20. }
  21. }
  22. }

我传过来是个String字符串,当做对象来解码

系统显示打印出了我发过来的字符串,然后又执行了一遍就报错了。

nett资料上如下图:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JpYmlib3l4_size_16_color_FFFFFF_t_70

大概意思就是说,这个ByteBuf还是是netty的那个传数据流的容器,这个List 是最终要返回给下面handler用的东西

传数据时这个decode方法会一直被调用,直到ByteBuf里面没有可以读取的数据,并且List也不是null就会传给下面的handler了。

那重点就是这俩:1.直到ByteBuf里面没有可以读取的数据 2.并且List也不是null

符合这俩条件,decode就会把数据交给handler。

2.问题我好理解,我out.add了,List已经不是null了。

1.问题就是怎么知道ByteBuf里有没有数据了?

那得看ByteBuf咋工作了:

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2JpYmlib3l4_size_16_color_FFFFFF_t_70 1

代码里,先bytebuf读取一下里面可以读取的字节容量,然后判断了是否大于4,如果大于4就表示我可以进行解析然后往List里添加了数据了。

然后通过String的构造方法把byte数组转换成String字符串,起始是0,截止是Bytebuf里放的流数据长度。

通过打断点发现,这个方法会在数据刚进入解码是调用一次,读取到可读长度,赋值给oldInputLength;之后会在最后结束此次解码时候再进去一次,if (outSize == out.size()) 这就是判断有没往out里加入数据,初始 outSize = 0;if (oldInputLength == in.readableBytes()) 这个是判断你有没有去把ByteBuf in里的东西给读出来。如果还是和之前赋值的oldInputLength一样的大小,那就表明没有读取过。这样就会抛出以上的异常。

  1. protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
  2. try {
  3. while(true) {
  4. if (in.isReadable()) {
  5. int outSize = out.size();
  6. if (outSize > 0) {
  7. fireChannelRead(ctx, out, outSize);
  8. out.clear();
  9. if (ctx.isRemoved()) {
  10. return;
  11. }
  12. outSize = 0;
  13. }
  14. int oldInputLength = in.readableBytes();
  15. this.decodeRemovalReentryProtection(ctx, in, out);
  16. if (!ctx.isRemoved()) {
  17. if (outSize == out.size()) {
  18. if (oldInputLength != in.readableBytes()) {
  19. continue;
  20. }
  21. } else {
  22. if (oldInputLength == in.readableBytes()) {
  23. throw new DecoderException(StringUtil.simpleClassName(this.getClass()) + ".decode() did not read anything but decoded a message.");
  24. }
  25. if (!this.isSingleDecode()) {
  26. continue;
  27. }
  28. }
  29. }
  30. }
  31. return;
  32. }
  33. } catch (DecoderException var6) {
  34. throw var6;
  35. } catch (Exception var7) {
  36. throw new DecoderException(var7);
  37. }
  38. }

结合Buf的原理,readindex如果不处理就会每次读取重复的数据,所以官方加判断并抛异常解决了这个死循环问题。

所以最常见的办法就是加上:skipBytes方法,将readindex往前移动数个位置

  1. 如:byteBuf.skipBytes(byteBuf.readableBytes());

下次直接从移动后的位置开始往后读取,就不会造成重复读取数据了

网上有人说要备份一下,否则影响其他decoder,这个还不理解为什么,这里mark一下

大概意思就是说,这个decoder把readindex往前移动了,但是其他decoder不能被影响,也要从头解析一遍,是这个意思吧

https://blog.csdn.net/zougen/article/details/79047252?utm_source=blogxgwz0

发表评论

表情:
评论列表 (有 0 条评论,70人围观)

还没有评论,来说两句吧...

相关阅读