netty-粘包拆包问题
粘包拆包问题
原因
- 底层缓冲区的大小与应用层发送的数据大小不匹配。
- 具体表现为:
- 应用层发送的数据量大于缓冲区大小。
- TCP层对报文进行分段,MSS(最大报文段大小,Maximum Segment Size)
- IP层限制了最大的传输单元,MTU(最大传输单元,Maximum Transmission Unit,MTU)
解决方案
- 定长消息:固定每个报文的长度,不足位补零。
- 由于有最大报文段的限制,无法处理长报文。
- 分隔符:在包结尾添加特定的分隔符。
- 设计消息头+消息体:消息头中包含报文长度。
- 需要预先知道数据长度。
- 自定义应用层协议
解码器
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
浙公网安备 33010602011771号