Netty编程(七)—— 粘包半包
粘包现象
以下面这个例子来说一下什么是粘包,下面这个是一个客户端的代码,它的任务很简单,就是向服务端连发十次0到15(十进制的字节00到0f):
public class StudyClient {
static final Logger log = LoggerFactory.getLogger(StudyClient.class);
public static void main(String[] args) {
NioEventLoopGroup worker = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class);
bootstrap.group(worker);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
log.debug("connected...");
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.debug("sending...");
// 每次发送16个字节的数据,共发送10次
for (int i = 0; i < 10; i++) {
ByteBuf buffer = ctx.alloc().buffer();
buffer.writeBytes(new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15});
ctx.writeAndFlush(buffer);
}
}
});
}
});
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 8080).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("client error", e);
} finally {
worker.shutdownGracefully();
}
}
}
按理来说服务端应该收到十条消息,每条消息都是0到15(00到0f),但实际上结果是下面的:
可见虽然客户端是分别以16字节为单位,通过channel向服务器发送了10次数据,可是服务器端却只接收了一次,接收数据的大小为160B,即客户端发送的数据总大小,这就是粘包现象
半包现象
将客户端-服务器之间的channel容量进行调整:
// 调整channel的容量
serverBootstrap.option(ChannelOption.SO_RCVBUF, 10);
注意
serverBootstrap.option(ChannelOption.SO_RCVBUF, 10) 影响的底层接收缓冲区(即滑动窗口)大小,仅决定了 netty 读取的最小单位,netty 实际每次读取的一般是它的整数倍
这个时候的结果是:
可见客户端每次发送的数据,因channel容量不足,无法将发送的数据一次性接收,便产生了半包现象
现象分析
粘包
- 现象
- 发送 abc、def,接收 abcdef
- 原因
- 应用层
- 接收方 ByteBuf 设置太大(Netty 默认 1024)
- 传输层-网络层
- 滑动窗口:假设发送方 256 bytes 表示一个完整报文,但由于接收方处理不及时且窗口大小足够大(大于256 bytes),这 256 bytes 字节就会缓冲在接收方的滑动窗口中, 当滑动窗口中缓冲了多个报文就会粘包
- Nagle 算法(如果一次发送的数据比较少,就等待其他数据一起发):会造成粘包
- 应用层
半包
- 现象
- 发送 abcdef,接收 abc、def
- 原因
- 应用层
- 接收方 ByteBuf 小于实际发送数据量
- 传输层-网络层
- 滑动窗口:假设接收方的窗口只剩了 128 bytes,发送方的报文大小是 256 bytes,这时接收方窗口中无法容纳发送方的全部报文,发送方只能先发送前 128 bytes,等待 ack 后才能发送剩余部分,这就造成了半包
- 数据链路层
- MSS 限制(一次能发送最大长度 ):当发送的数据超过 MSS 限制后,会将数据切分发送,就会造成半包
- 应用层
本质
发生粘包与半包现象的本质是因为 TCP 是流式协议,消息无边界