Netty向ChannelPipeline中添加多个Decoder不生效的问题

ByteToMessageDecoder解码器

业务逻辑如下:

  1. 自定义Decoder拿到tcp的数据,按照支持的协议标记去匹配确定该数据采用的协议。
  2. 将协议标记绑定到channel的attr中。
  3. 向当前的ChannelPipeline中添加协议指定的Decoder。

代码大概逻辑如下:

NettyServer:

.......
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("protocolDecoder", new ProtocolDecoder());
pipeline.addLast("businessHandler", new BusinessHandler());
.......

ProtocolDecoder:

public class ProtocolDecoder extends ByteToMessageDecoder {
   @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> out) throws Exception {
      int len = byteBuf.readableBytes();
      // 包头做标记位,后面可以重新回到数据包头开始读数据
      byteBuf.markReaderIndex();
      // 有数据时开始读数据包
       byte[] src = new byte[len];
       //复制数据至数组
      byteBuf.getBytes(byteBuf.readerIndex(), src);
      //数据处理判断协议的逻辑代码, 省略
      .........
      //根据协议添加特定的粘包拆包处理Decoder
      pipeline.addAfter("protocolDecoder","lengthFieldBasedFrameDecoder", new LengthFieldBasedFrameDecoder(1024, 1, 1));
      //由于在此向out数组中添加的非ByteBuf对象,导致LengthFieldBasedFrameDecoder的decode不生效
      //out.add(src);//错误
      out.add(byteBuf);//正确
    }
}

原因:

//经过debug发现触发decode方法的大概流程如下
/*
 *首先经过AbstractChannelHandlerContext的invokeChannelRead方法
 *->触发具体的ByteToMessageDecoder的实现对象的channelRead方法
*/
//chanelRead方法部分源码如下:
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {//1
          ........
          try {
            ..........
            fireChannelRead(ctx, out, size);//2
          } finally {
                out.recycle();
          }
        }else{
          ctx.fireChannelRead(msg);//3
        }
}

导致编写的动态添加的粘包拆包Decoder生效的原因是:由于向out数组中添加的非ByteBuf对象导致上述方法时没有调用#2的代码。因此在pipeline包含多个ByteToMessageDecoder时应该保证非最后一个的out数组中添加的均为ByteBuf对象。

经过测试,上述解决方案能够解决多个Decoder的问题,但是不知道为什么handler处理完之后会报异常;技术太菜debug了很久也没找出来问题在哪,没办法只能改思路了 (有知道的大佬希望能点拨一下,非常感谢 !)

修改方案如下,不用两个Decoder了而是将LengthFieldBasedFrameDecoder源码修改成自己想要的模式:

创建新的Decoder复制LengthFieldBasedFrameDecoder源码然后修改decode方法

public class UnpackingDecoder extends ByteToMessageDecoder {
  //LengthFieldBasedFrameDecoder源码内容
  //.......................
  //源码方法
  protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    //以下是添加部分
    int len = in.readableBytes();
    // 包头做标记位,后面可以重新回到数据包头开始读数据
    in.markReaderIndex();
    // 有数据时开始读数据包
    byte[] src = new byte[len];
    in.getBytes(in.readerIndex(), src);
    //根据src数据按照指定的协议规则判断该channel属于哪个协议,然后将该协议标志绑定至channel
    //绑定完成后动态给当前解码器对象设置属性值
    initProperties(当前协议指定的包解析属性);
    //源码部分
    Object decoded = this.decode(ctx, in);
    if (decoded != null) {
      //这里为添加逻辑,将bytebuf转换成tcprequest对象进行业务处理
      TcpRequest tcpRequest = new TcpRequest(src, protocol);
      out.add(tcpRequest);
    }    
  }
  
  //添加方法
  private void initProperties(ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
        this.byteOrder = (ByteOrder) ObjectUtil.checkNotNull(byteOrder, "byteOrder");
        ObjectUtil.checkPositive(maxFrameLength, "maxFrameLength");
        ObjectUtil.checkPositiveOrZero(lengthFieldOffset, "lengthFieldOffset");
        ObjectUtil.checkPositiveOrZero(initialBytesToStrip, "initialBytesToStrip");
        if (lengthFieldOffset > maxFrameLength - lengthFieldLength) {
            throw new IllegalArgumentException("maxFrameLength (" + maxFrameLength + ") must be equal to or greater than lengthFieldOffset (" + lengthFieldOffset + ") + lengthFieldLength (" + lengthFieldLength + ").");
        } else {
            this.maxFrameLength = maxFrameLength;
            this.lengthFieldOffset = lengthFieldOffset;
            this.lengthFieldLength = lengthFieldLength;
            this.lengthAdjustment = lengthAdjustment;
            this.lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
            this.initialBytesToStrip = initialBytesToStrip;
            this.failFast = failFast;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值