1 TCP 粘包、拆包
TCP是一个流式协议,没有边界。TCP底层会按照TCP缓冲区内情况进行包的拆解,合并,就会产生粘包,拆包问题。
1.1 产生粘包、拆包的原因
- 应用程序 write 的字节大小大于缓冲区大小
- 进行 MSS 大小的TCP分段
- 以太网帧的 payload 大于MTU 进行 IP 分片
1.2 解决方案
- 消息定长
每次发送消息固定长度,不足长度空格补上,但是浪费空间
- 消息末端增加换行符进行消息切割
FTP协议
- 将消息分为消息头为消息体,消息头包含消息长度
2. netty 提供的解决方案(编码解码器)
- LineBasedFrameDecoder (换行符解码器)
A decoder that splits the received {@link ByteBuf}s on line endings.Both {@code “\n”} and {@code “\r\n”} are handled.
原理是依次遍历 ByteBuf 中的字节,直到读到换行符,丛可读位置到结束位置就组成一行内容。
构造器支持设定大小,当超过大小仍没有读到换行符,则被认为为异常消息内容,同时忽略掉之前读到的流。
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 换行符解码器
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new NettyServerHandler());
}
- DelimiterBasedFrameDecoder (分隔符解码器)
A decoder that splits the received {@link ByteBuf}s by one or more * delimiters.
用一个或多个*分隔符分割接收到的{@link ByteBuf}的解码器。
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 分隔符解码器
ByteBuf byteBuf = Unpooled.copiedBuffer("$_".getBytes());
// 第一个参数 1024 标识单条消息最大长度,达到最大长度后还没有找到分隔符,就抛出TooLongFrameException
// 第二个参数为缓冲区分割对象
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, byteBuf));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new NettyServerHandler());
}
- FixedLengthFrameDecoder(定长解码器)
A decoder that splits the received {@link ByteBuf}s by the fixed numbe * of bytes.
将接收到的{@link ByteBuf}按固定的字节数分割。
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 定长解码器 会按照构造函数内的大小进行数据读取
ch.pipeline().addLast(new FixedLengthFrameDecoder(100));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new NettyServerHandler());
}