Netty权威指南_札记04_TCP粘包/拆包问题解决

版权声明:士,不可以不弘毅,任重而道远 https://blog.csdn.net/superbeyone/article/details/83898458


Netty权威指南_札记04_TCP粘包/拆包问题解决

1. TCP粘包/拆包

概念:
TCP是个“流”协议,是没有界限的一串数据。因为TCP底层不了解上层业务数据的具体含义,它会根据TCP换从去的实际情况进行包的拆分,所以在业务上人为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送。

1.1 TCP粘包/拆包问题说明

假设客户端分别发送两个数据包D1和D2给服务器,由于服务器一次读取的字节数是不确定的,故可能存在以下四种情况:

  1. 服务端分两次读取到了两个独立的数据包,分别是D1和D2,没有粘包和拆包;
  2. 服务端一次接收到了两个数据包,D1和D2粘合在一起,被称为TCP粘包;
  3. 服务端分两次读取到了两个数据包,第一次读取到了完整的D1包和D2的部分内容,第二次读取到了D2包的部分内容,被称为TCP拆包;
  4. 服务器分两次读取到了两个数据包,第一次读取到了D1包的部分内容,第二次读取到了D1的剩余内容和D2整包。

注:其实就是类似于滑块问题

1.2 TCP粘包/拆包发生的原因

  1. 应用程序write写入的子节大小大于套接口发送缓冲区大小;
  2. 进行MSS大小的TCP分段;
  3. 以太网帧的payload大于MTU进行IP分片。

1.3 粘包问题解决策略

  1. 消息定长;
  2. 在包尾增加回车换行符进行分割,例如FTP协议;
  3. 将消息分为消息头和消息体,消息头中包含消息总长度或者消息体长度;
  4. 更复杂的应用层协议。

2. 利用LineBasedFrameDecoder解决TCP粘包问题

2.1 服务端

Netty时间服务器服务端 TimeServer
代码改造:

 ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, work)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //增加部分 start
                            socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                            socketChannel.pipeline().addLast(new StringDecoder()); 
                            //增加部分 end                                   
                            socketChannel.pipeline().addLast(new TimeServerHandler());
                        }
                    });

2.2 客户端

Netty时间服务器客户端 TimeClient
代码改造:

 ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                         	//增加部分 start
                            socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                            socketChannel.pipeline().addLast(new StringDecoder()); 
                            //增加部分 end
                            socketChannel.pipeline().addLast(new TimeClientHandler());
                        }
                    });

猜你喜欢

转载自blog.csdn.net/superbeyone/article/details/83898458