TCP 是面向流的,提供高可靠性服务。收发两端都要有成对的 Socket
因此发送端为了将多个发给接收端的包,更有效的发送给对方,使用了 Nagle 算法优化
将多次间隔较小且数量小的数据,合并为一个大的数据块,然后进行封包,
这样虽然提高了效率,但是接收端就难以分辨出完整的数据包了,因为面向流的通信是无消息保护边界的
粘包
现象
发送 abc
def
,接收为 abcdef
原因
- 应用层:接收方 ByteBuf 设置太大(Netty默认1024)
- 滑动窗口:假设发送方256bytes表示一个完整的报文,但由于接收方处理不及时且窗口大小足够大,这256bytes字节就会缓冲在接收方的滑动窗口中,当滑动窗口中缓冲了多个报文就会出现粘包
- Nagle 算法
半包
现象
发送 abcdef
接收 abc
de
f
原因
-
应用层:接收方 ByteBuf 小于实际发送数据的数量
-
滑动窗口:加涉接收方的窗口只剩128bytes,发送方的报文大小是256bytes,这时放不下了,只能先发送前128bytes,等待ack后才能发送剩余部分,这就造成半包
-
MSS限制:当发送的数据草果 MSS 限制后,会将数据切分发送,就会照成半包
粘包半包演示
服务端
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
logger.info("客户端发送的消息是 : {}", buf.toString(CharsetUtil.UTF_8));
}
复制代码
客户端
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 10; i++) {
ByteBuf buf = Unpooled.copiedBuffer("Hello Client" + i, CharsetUtil.UTF_8);
ctx.writeAndFlush(buf);
}
}
复制代码
结果打印
[nioEventLoopGroup-3-1] INFO io.mvvm.netty.decoder.NettyServerHandler - 客户端发送的消息是 : Hello Client0Hello Client1Hello Client2Hello Client3Hello Client4Hello Client5Hello Client6Hello Client7Hello Client8Hello Client9
复制代码
结论
代码解释
客户端和服务端建立连接后,循环向服务端发送10条消息
期望结果
服务端收到10条消息
实际结果
服务端将10条消息放在一起成为了一条消息,这就是粘包现象
解决方案
短连接
建立连接后只发送一次消息,发送消息完毕后断开连接
缺点:不如用http
定长解码器 FixedLengthFrameDecoder
定常解码器即固定每条消息的固定长度,如果消息长度小于定长的长度,则进行补位到定长长度
<