详解什么是 TCP 粘包和拆包现象并演示 Netty 是如何解决的( 二 )

< 10000; i++) {stringBuilder.Append(data);}ByteBuf buffer = Unpooled.copiedBuffer("数据" + stringBuilder.toString(), StandardCharsets.UTF_8);ctx.writeAndFlush(buffer);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {ctx.close();}}其中关键的代码如下
// 重写 channelActive, 当客户端启动的时候 自动发送数据给服务端@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {// 客户端只发送一次 , 但是本次数据量很大// tcp 会将数据拆分成多份后依次进行发送String data = https://www.isolves.com/it/wl/zs/2022-04-24/"ckjava";StringBuilder stringBuilder = new StringBuilder(data);for (int i = 0; i < 10000; i++) {stringBuilder.append(data);}ByteBuf buffer = Unpooled.copiedBuffer("数据" + stringBuilder.toString(), StandardCharsets.UTF_8);ctx.writeAndFlush(buffer);}

  1. Netty 客户端启动类:NettyClient
package com.ckjava.test.server;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import java.net.InetSocketAddress;@Slf4j@Componentpublic class NettyServer {private final int port;public NettyServer(int port) {this.port = port;}public void start() {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap sbs = new ServerBootstrap().group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port)).childHandler(new ChannelInitializer<SocketChannel>() {protected void initChannel(SocketChannel ch) {ch.pipeline().addLast(new NettyServerHandler());}}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);// 绑定端口 , 开始接收进来的连接sbs.bind(port).addListener(future -> {log.info(String.format("服务器端启动成功 , 开放端口:%s", port));});} catch (Exception e) {log.error("启动服务器端出现异常", e);}}public static void main(String[] args) {int port = 8080;new NettyServer(port).start();}}
  1. Netty 服务器端通道处理类:NettyServer
package com.ckjava.test.server;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;import lombok.extern.slf4j.Slf4j;import java.nio.charset.StandardCharsets;import java.util.UUID;@Slf4jpublic class NettyServerHandler extends SimpleChannelInboundHandler<ByteBuf> {private int counter;private int count;@Overrideprotected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {byte[] buffer = new byte[msg.readableBytes()];msg.readBytes(buffer);//将buffer转为字符串String message = new String(buffer, StandardCharsets.UTF_8);System.out.println("服务器收到的数据=" + message);System.out.println("服务器收到的数据次数=" + (++this.count));//服务器回送数据给客户端 回送一个随机idByteBuf buffer1 = Unpooled.copiedBuffer(UUID.randomUUID().toString(), StandardCharsets.UTF_8);ctx.writeAndFlush(buffer1);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}}
  1. 分别先启动服务器端后 , 再启动客户端 , 服务器端的输出如下

详解什么是 TCP 粘包和拆包现象并演示 Netty 是如何解决的

文章插图
 
  1. 客户端的输出如下
17:03:24.474 [nioEventLoopGroup-2-1] INFOcom.ckjava.test.client.NettyClient - 连接服务器端:127.0.0.1:8080 成功!17:03:24.535 [nioEventLoopGroup-2-1] INFOc.c.test.client.NettyClientHandler - 客户端接收到消息:[c471a239-abe5-4401-93aa-b3d5e432c422021b6ae3-4939-4d59-b451-235af6c9e2190536b0aa-3b53-4b03-bb68-b0637d619d0f]17:03:24.537 [nioEventLoopGroup-2-1] INFOc.c.test.client.NettyClientHandler - 客户端接收到消息的次数:117:03:24.537 [nioEventLoopGroup-2-1] INFOc.c.test.client.NettyClientHandler - ---------------------------------------------------
  1. 从服务器端和客户端的输出结果来看:客户端只发送了 1 次数据 , 但是服务器端却收到了 3 次数据 , 说明 tcp 在客户端拆包后分 3 次发送了;并且客户端之后只收到了一次数据 , 说明服务器的回复数据在服务器端也出现了粘包现象 , 并且导致了数据无法区分的问题 。
通过 Netty 重现 TCP 粘包现象


推荐阅读