netty-粘包拆包问题

粘包拆包问题

原因

  • 底层缓冲区的大小与应用层发送的数据大小不匹配。
  • 具体表现为:
    • 应用层发送的数据量大于缓冲区大小。
    • TCP层对报文进行分段,MSS(最大报文段大小,Maximum Segment Size)
    • IP层限制了最大的传输单元,MTU(最大传输单元,Maximum Transmission UnitMTU

解决方案

  • 定长消息:固定每个报文的长度,不足位补零。
    • 由于有最大报文段的限制,无法处理长报文。
  • 分隔符:在包结尾添加特定的分隔符。
  • 设计消息头+消息体:消息头中包含报文长度。
    • 需要预先知道数据长度。
  • 自定义应用层协议

解码器

DelimiterBasedFrameDecoder

分隔符解码器

  • 发送数据时:需要在结尾加上特定的分隔符,例如“\t”
ByteBuf delimiter = Unpooled.copiedBuffer("\t".getBytes());//声明分隔符
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(2048,delimiter));//2048,指定最大帧长度,超过该长度将破抛出异常TooLongFrameException    

FixedLengthFrameDecoder

定长解码器

  • 如果消息是半包,则缓存消息,知道获取到一个完整长度的包。
ch.pipeline().addLast(new FixedLengthFrameDecoder(1024));

空位补全方式

遍历补空格

			/**
             * 方式一:循环遍历,对于不满的,直接添加空格补齐
             */
            String s1 = "我是客户端-123";
            byte[] bytes1 = s1.getBytes("UTF-8");
            byte[] msgBytes1 = new byte[64];
            for (int i = 0; i < msgBytes1.length; i++) {
                if (i < bytes1.length) {
                    msgBytes1[i] = bytes1[i];
                } else {
                    /**32 表示空格,等价于:msgBytes1[i] = " ".getBytes()[0];*/
                    msgBytes1[i] = 32;
                }
            }

数组拷贝

			 /**
             * 方式二:使用 System.arraycopy 快速复制数组
             */
            String s2 = "我是客户端-123";
            byte[] bytes2 = s2.getBytes("UTF-8");
            byte[] msgBytes2 = new byte[64];
            System.arraycopy(bytes2, 0, msgBytes2, 0, bytes2.length);
            System.out.println(new String(msgBytes2) + "," + msgBytes2.length);

java序列化方案

服务端

 ch.pipeline().addLast(new ObjectEncoder());
                    ch.pipeline().addLast(new ObjectDecoder(Integer.MAX_VALUE, // 最大长度
                                                     ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader()))); 
                
    /**
     * Creates a new decoder with the specified maximum object size.
     *
     * @param maxObjectSize  the maximum byte length of the serialized object.
     *                       if the length of the received object is greater
     *                       than this value, {@link StreamCorruptedException}
     *                       will be raised.
     * @param classResolver    the {@link ClassResolver} which will load the class
     *                       of the serialized object
     */
public ObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
        super(maxObjectSize, 0, 4, 0, 4);
        this.classResolver = classResolver;
    }
  • 添加解码器:io.netty.handler.codec.serialization.ObjectDecoder;
    • 将对实现了Serializable接口的POJO解码。
    • 通过ClassResolver加载类。
  • 添加编码器:io.netty.handler.codec.serialization.ObjectEncoder;
    • 发送消息时,自动将实现了Serializable接口的POJO编码。
  • POJO实现序列化接口Serializable
posted @ 2021-03-14 16:10  code汤  阅读(97)  评论(0)    收藏  举报