概述
TCP传输协议是面向连接的,面向流提供高可靠的服务。收发两端(服务端和客户端)都要有一一成对的socket,因此,发送端为了将多个发给接收端的包,更有效地发给对方,使用了优化算法(Nagle算法),将多次间隔时间较小且数据较小的数据包,合成一个大的数据块,然后进行封包,这样做虽然提高了传输的效率,但是这样接收端就难以分辨出一个个完整的包的大小了,因为面向流的通信时无消息保护边界的。
由于TCP无消息保护边界,需要在接收端处理消息边界问题,也就是粘包、拆包问题。
示意图:

说明:
- 服务端给客户端发送D1、D2两个数据包。
- 第一种情况是服务端两次都读取到的是两个独立的数据包,无拆包粘包问题,可以正常解析。
- 第二种情况D1、D2包合并在一起一次发送,服务端一次性接收了两个数据包,分别是D1、D2,我们称之为粘包,因为不知道边界,所以服务端不知如何拆出来解析。
- 第三种情况,服务端第一次接收到了完整D1包和部分D2包,第二次接收到了剩余的D2包,D2包被拆开来分多次发送了,我们称之为拆包。
- 第四种情况,服务端第一次接收到了部分D1包,第二次接收到了剩余部分D1包和完整D2包,也称之为拆包。
总的来说拆包和粘包问题,因为没有边界,最终会在正常情况下导致接收方无法分辨出一个一个包。
粘包实例
以下实例证实了粘包的存在:
服务端及服务端的Handler:
public class NettyTcpServer {
private int port;
public NettyTcpServer(int port){
this.port = port;
}
public void start() throws InterruptedException {
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new NettyTcpServerChannelHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(port);
channelFuture.channel().closeFuture().sync();
}
public static void main(String[] args) throws InterruptedException {
NettyTcpServer nettyTcpServer = new NettyTcpServer(8989);
nettyTcpServer.start();
}
}
public class NettyTcpServerChannelHandler extends ChannelInboundHandlerAdapter {
private int count;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("数据是:" + byteBuf.toString(CharsetUtil.UTF_8));
System.out.println("count = " + (++count));
super.channelRead(ctx, msg);
}
}
客户端及客户端的Handler
public class NettyTcpClient {
private int serverPort;
public NettyTcpClient(int serverPort){
this.serverPort = serverPort;
}
public void start() throws InterruptedException {
NioEventLoopGroup worker = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap

本文围绕Netty网络通信展开,介绍了TCP传输中因无消息保护边界导致的粘包、拆包问题。通过实例和演示展示了粘包、拆包现象,最后提出使用自定义协议加编码解码器的解决方案,经测试成功解决了粘包和拆包问题。
最低0.47元/天 解锁文章
832





