Netty笔记(五)之编解码器与粘包拆包

本文详细介绍了Netty中的ByteToMessageDecoder处理TCP粘包和拆包问题,包括粘包的原因和解决方案,如定长解码器、回车换行符分隔和特殊分隔符解码器。同时,展示了没有解码器时的案例及使用解码器后的效果,并提及了ReplayingDecoder的作用。此外,还提及了MessageToByteEncoder在编码器方面的应用。
摘要由CSDN通过智能技术生成

netty版本

  1. netty版本:io.netty:netty-all:4.1.33.Final

解码

  1. 将字节解码为消息或者另一个字节序列是一项常见的任务,Netty提供了一个基类ByteToMessageDecoder,由于无法知道消息字节会发送多少次才能发送完毕,所以这个类会对入站数据进行缓冲,直到数据已经完整到达。

ByteToMessageDecoder

  1. 方法

    方法 描述
    decode(ChannelHandlerContext ctx, ByteBuf in, List out) 这是你必须实现的唯一抽象方法。decode()方法被调用时将会传入一个包含了传入数据的ByteBuf,以及一个用来添加解码消息的List。对这个方法的调用将会重复进行,直到确定没有新的元素被添加到该 List,或者该ByteBuf中没有更多可以读取的字节时为止。然后 ,如果该List不为空,那么它的内容将会被传递给ChannelPipeline中的下一个 ChannelInboundHandler
    decodeLast(ChannelHandlerContext ctx, ByteBuf in, List out) Netty提供的这个默认实现只是简单地调用了 decode()方法。 当Channel的状态变为非活动时,这个方法将会被调用一次。可以重写该方法以提供特殊的处理,比如在Channel关闭之后产生最后一个消息
  2. 对于编码解码器,一旦消息被编码或者解码,就会被ReferenceCountUtil.release(msg)调用自动释放。如果需要保留引用,可以调用ReferenceCountUtil.retain(msg)方法来增加引用计数,防止消息被释放

TCP粘包与拆包

  1. TCP是一个流协议,所谓流就是一个没有界限的一串数据,TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际数据进行包的划分,一个完整的数据包可能会被拆分成多个TCP包进行发送,也有可能把多个小的数据包封装成一个大的TCP包进行发送

  2. 为什么会粘包?

    • 在用户数据量非常小的情况下,极端情况下,一个字节,该TCP数据包的有效载荷非常低,传递100字节的数据,需要100次TCP传送,100次ACK,在应用及时性要求不高的情况下,将这100个有效数据拼接成一个数据包,那会缩短到一个TCP数据包,以及一个ACK,有效载荷提高了,带宽也节省了

    • 粘包现象

             写入数据
          +------+   +------+
          | MSG1 |   | MSG2 |
          +------+   +------+
          发送数据
          +------------+
          | MSG1  MSG2 |
          +------------+
          接收到的数据
          +----+     +---------+      +-------+    +-----+ 
          | MS |     |  G1MSG2 |  或者 | MSG1M |    | SG2 | 
          +----+     +---------+      +-------+    +-----+
      
  3. 解决方案:通过应用协议对消息进行区分

    • 消息长度固定,累计读取到的长度总和为定长LENGTH的报文后,就认为读取到了一个完整的消息,此时将计数器置位,重新开始读取下一个数据包
    • 在包尾增加一个特殊分隔符,通过这个标志进行分割,比如回车换行符
    • 通过在消息头中定义长度字段来标识消息的总长度
  4. netty针对以上的解决方案

    • FixedLengthFrameDecoder:定长解码器来解决定长消息的粘包问题
    • LineBasedFrameDecoder:解决以回车换行符作为消息结束符的TCP粘包的问题;
    • DelimiterBasedFrameDecoder:特殊分隔符解码器来解决以特殊符号作为消息结束符的TCP粘包问题;
    • LengthFieldBasedFrameDecoder自定义长度解码器解决TCP粘包问题。
  5. 拆包的原理

    • 如果当前读取的数据不足以拼接成一个完整的业务数据包,那就保留该数据,继续从tcp缓冲区中读取,直到得到一个完整的数据包
    • 如果当前读到的数据加上已经读取的数据足够拼接成一个数据包,那就将已经读取的数据拼接上本次读取的数据,够成一个完整的业务数据包传递到业务逻辑,多余的数据仍然保留,以便和下次读到的数据尝试拼接

没有解码器的案例

  1. 客户端Handler代码

        public class TimeClientHandler extends ChannelInboundHandlerAdapter {
         
        
            private static final String LINE = System.getProperty("line.separator");
        
            private static final String REQUEST_DATA = "this is message from client!";
        
            private static final Logger LOGGER = LoggerFactory.getLogger(TimeClientHandler.class);
        
            private volatile int counter;
            private byte[] req;
        
        
            public TimeClientHandler() {
         
                req = (REQUEST_DATA + LINE).getBytes();
            }
        
            /**
             * 向服务端连续发送100 条this is message from client!\n
             */
            @Override
            public void channelActive(ChannelHandlerContext ctx) {
         
                ByteBuf message = null;
                for (int i = 0; i < 100; i++) {
         
                    message = Unpooled.buffer(req.length);
                    message.writeBytes(req);
                    ctx.writeAndFlush(message);
                }
            }
        
        
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
         
                ByteBuf buf = (ByteBuf) msg;
                byte[] req = new byte[buf.readableBytes()];
                buf.readBytes(req);
                String body = new String(req, "UTF-8");
                System.out.println("服务器发送给客户端的数据是:" + body + " ;总共次数是: " + (++counter));
            }
        
            @Override
            public void exceptionCaught(ChannelHandlerCont
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值