粘包拆包
TCP是面向连接的,面向流的,提供了可靠的连接。
发送端为了提高效率,采用了Nagle算法,将多次间隔小数据量小的包,合并成一个大的数据块,然后进行分
包。
这样做虽然提高了效率,但是接收端无法分辨出完整的数据包。因为面向流的通信是无消息保护边界的。
TCP无消息保护边界,需要在接收端处理消息边界问题,也就是粘包拆包。
粘包:发送两个数据包,前一个数据包和后一个数据包粘在一起
拆包:发送一个数据包,接收端分两段接收。
解决方案
发送数据时,把此次发送的数据的长度也发送过去。根据长度了接收数据,就不会出现多读或者少读的问题。
示例:
存放数据的类
public class Message {
// byte数组的长度
private int len;
private byte[] content;
// getter setter
}
解码器
public class MessageDecoder extends ReplayingDecoder<Void> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
System.out.println("Decoder被调用");
int len = in.readInt();
byte[] content = new byte[len];
// 此处不需要判断readableBytes >= len,ReplayingDecoder会自动根据长度判断
in.readBytes(content);
Message message = new Message(len, content);
out.add(message);
}
}
编码器
public class MessageEncoder extends MessageToByteEncoder<Message> {
@Override
protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws Exception {
System.out.println("MessageEncoder被调用");
int len = msg.getLen();
byte[] content = msg.getContent();
out.writeInt(len);
out.writeBytes(content);
}
}
添加编码器和解码器
pipeline.addLast(new MessageDecoder());
pipeline.addLast(new MessageEncoder());
发送
// 发送
byte[] content = str.getBytes(CharsetUtil.UTF_8);
int len = content.length;
Message message = new Message(len, content);
ctx.writeAndFlush(message);