范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

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

  概述
  本文介绍什么是 TCP 粘包和拆包现象,并通过 Netty 编写详细的案例来重现 TCP 粘包问题,最后再通过一个 Netty 的 demo 来解决这个问题。具体内容如下 什么是 TCP 粘包和拆包现象 重现 TCP 粘包和拆包现象 Netty 解决 TCP 粘包和拆包现象带来的问题 什么是 TCP 粘包和拆包现象
  TCP 编程底层都有粘包和拆包机制,因为我们在C/S这种传输模型下,以TCP协议传输的时候,在网络中的byte其实就像是河水,TCP就像一个搬运工,将这流水从一端转送到另一端,这时又分两种情况: 如果客户端的每次制造的水比较多,也就是我们常说的客户端给的包比较大,TCP这个搬运工就会分多次去搬运 如果客户端每次制造的水比较少的话,TCP可能会等客户端多次生产之后,把所有的水一起再运输到另一端 对于第一种情况,TCP 会再客户端先进行拆包,在另一端接收的时候,需要把多次获取的结果组合在一起,变成我们可以理解的信息 对于第二种情况,TCP 会在客户端先进行粘包,在另一端接收的时候,就必须进行拆包处理,因为每次接收的信息,可能是另一个远程端多次发送的包,被TCP粘在一起的 重现 TCP 粘包和拆包现象通过在客户端 1 次发送超大数据包给服务器端来重现 TCP 拆包现象 通过在客户端分 10 次发送较小的数据包给服务器端来重现 TCP 粘包现象
  下面通过 Netty 重现 TCP 粘包和拆包现象。 Netty maven 依赖 	io.netty 	netty-all 	4.1.76.Final  通过 Netty 重现 TCP 拆包现象Netty 客户端启动类:NettyClient package com.ckjava.test.client;  import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component;  @Slf4j @Component public class NettyClient {     static final String HOST = System.getProperty("host", "127.0.0.1");     static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));     static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));      public static void main(String[] args) throws Exception {         // 初始化客户端事件组         EventLoopGroup group = new NioEventLoopGroup();         try {             Bootstrap b = new Bootstrap();             b.group(group)                     // 初始化通道                     .channel(NioSocketChannel.class)                     .option(ChannelOption.TCP_NODELAY, true)                     .handler(new ChannelInitializer() {                         // 初始化通道处理器                         @Override                         public void initChannel(SocketChannel ch) {                             ChannelPipeline p = ch.pipeline();                             p.addLast(new NettyClientHandler());                         }                     });              b.connect(HOST, PORT).addListener(future -> {                 log.info(String.format("连接服务器端:%s:%s 成功!", HOST, PORT));             }).await();         } catch (Exception e) {             log.error("启动客户端出现异常", e);         }     } } Netty 客户端通道处理类:NettyClientHandler package com.ckjava.test.client;  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.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger;  @Slf4j public class NettyClientHandler extends SimpleChannelInboundHandler {      private final AtomicInteger countRef = new AtomicInteger(0);      //客户端读取服务器发送的信息     @Override     protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {         byte[] buffer = new byte[msg.readableBytes()];         msg.readBytes(buffer);         String message = new String(buffer, StandardCharsets.UTF_8);         log.info(String.format("客户端接收到消息:[%s]", message));         log.info(String.format("客户端接收到消息的次数:%s", countRef.accumulateAndGet(1, Integer::sum)));         log.info("---------------------------------------------------");     }      // 重写 channelActive, 当客户端启动的时候 自动发送数据给服务端     @Override     public void channelActive(ChannelHandlerContext ctx) throws Exception {         // 客户端只发送一次,但是本次数据量很大         // tcp 会将数据拆分成多份后依次进行发送         String data = "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);     }      @Override     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {         ctx.close();     }  }
  其中关键的代码如下  // 重写 channelActive, 当客户端启动的时候 自动发送数据给服务端 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception {     // 客户端只发送一次,但是本次数据量很大     // tcp 会将数据拆分成多份后依次进行发送     String data = "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); } 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 @Component public 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() {                         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();     } } 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;  @Slf4j public class NettyServerHandler extends SimpleChannelInboundHandler {      private int counter;      private int count;      @Override     protected 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));          //服务器回送数据给客户端 回送一个随机id         ByteBuf buffer1 = Unpooled.copiedBuffer(UUID.randomUUID().toString(), StandardCharsets.UTF_8);         ctx.writeAndFlush(buffer1);     }       @Override     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {         cause.printStackTrace();         ctx.close();     }  } 分别先启动服务器端后,再启动客户端,服务器端的输出如下
  客户端的输出如下 17:03:24.474 [nioEventLoopGroup-2-1] INFO  com.ckjava.test.client.NettyClient - 连接服务器端:127.0.0.1:8080 成功! 17:03:24.535 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[c471a239-abe5-4401-93aa-b3d5e432c422021b6ae3-4939-4d59-b451-235af6c9e2190536b0aa-3b53-4b03-bb68-b0637d619d0f] 17:03:24.537 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:1 17:03:24.537 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 从服务器端和客户端的输出结果来看:客户端只发送了 1 次数据,但是服务器端却收到了 3 次数据,说明 tcp 在客户端拆包后分 3 次发送了;并且客户端之后只收到了一次数据,说明服务器的回复数据在服务器端也出现了粘包现象,并且导致了数据无法区分的问题。 通过 Netty 重现 TCP 粘包现象还用上面的例子,将客户端通道处理类:NettyClientHandler 中的 channelActive 方法修改成如下的方式 // 重写 channelActive, 当客户端启动的时候 自动发送数据给服务端 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception {     // 使用客户端分10次发送,每次数据量少     // tcp 会等客户端多次生产后,一次性进行发送     for (int i = 0; i < 10; i++) {         ByteBuf buffer = Unpooled.copiedBuffer("ckjava" + i + " ", StandardCharsets.UTF_8);         ctx.writeAndFlush(buffer);     } } 分别先启动服务器端后,再启动客户端,服务器端的输出如下 17:12:27.239 [nioEventLoopGroup-2-1] INFO  com.ckjava.test.server.NettyServer - 服务器端启动成功,开放端口:8080 服务器收到的数据=ckjava0 ckjava1 ckjava2 ckjava3 ckjava4 ckjava5 ckjava6 ckjava7 ckjava8 ckjava9  服务器收到的数据次数=1 客户端的输出如下 17:12:36.917 [nioEventLoopGroup-2-1] INFO  com.ckjava.test.client.NettyClient - 连接服务器端:127.0.0.1:8080 成功! 17:12:36.961 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[31b25c25-bd32-4ff1-b390-0c31b2558d12] 17:12:36.962 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:1 17:12:36.962 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 从服务器端和客户端的输出结果来看:客户端只发送了 10 次数据,但是服务器端却收到了 1 次数据,说明 tcp 在客户端粘包后一次性发送了全部的数据。 Netty 解决 TCP 粘包和拆包现象带来的问题TCP 粘包和拆包现象带来的问题
  从上面的案例可以发现当出现 TCP 粘包和拆包现象后会出现下面的问题: tcp 在粘包的时候,数据混合后,接收方不能正确区分数据的头尾,如果是文件类型的数据,会导致文件破坏。 tcp 在拆包的时候,数据拆分后,接收方不能正确区分数据的头尾,导致收到的消息错乱,影响语义。 如何解决 TCP 粘包和拆包现象带来的问题
  由于 TCP 粘包和拆包现象会导致不能正确区分数据的头尾,那么解决的办法也挺简单的,通过 特殊字符串 来分隔消息体或者使用 定长消息 就能够正确区分数据的头尾。
  目前的主流解决方式有以下几种: 使用定长消息,Client 和 Server 双方约定报文长度,Server 端接受到报文后,按指定长度解析; 使用特定分隔符,比如在消息尾部增加分隔符。Server 端接收到报文后,按照特定的分割符分割消息后,再解析; 将消息分割为消息头和消息体两部分,消息头中指定消息或者消息体的长度,通常设计中使用消息头第一个字段 int32 表示消息体的总长度;
  Netty 中也提供了基于分隔符实现的半包解码器和定长的半包解码器: LineBasedFrameDecoder 使用" "和"r "作为分割符的解码器 DelimiterBasedFrameDecoder 使用自定义的分割符的解码器 FixedLengthFrameDecoder 定长解码器 通过 Netty 的 DelimiterBasedFrameDecoder 解码器 来解决 TCP 粘包和拆包现象带来的问题
  使用 DelimiterBasedFrameDecoder 可以确保收到的数据会自动通过 自定义的分隔符 进行分隔。发送的时候消息的后面只需要增加上 自定义的分隔符 即可。 基于上面的例子,服务器端 NettyServer 改动如下 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() {                     protected void initChannel(SocketChannel ch) {                         // 使用分隔符"$_"的半包解码器                         ByteBuf byteBuf = Unpooled.copiedBuffer(DELIMITER.getBytes());                         ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, byteBuf));                         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);     } } 服务器端 NettyServerHandler 改动如下 @Override protected 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));      //服务器回送数据给客户端 回送一个随机id     String replyData = UUID.randomUUID().toString();     ByteBuf buffer1 = Unpooled.copiedBuffer(replyData.concat(NettyServer.DELIMITER), StandardCharsets.UTF_8);     ctx.writeAndFlush(buffer1);     System.out.println("服务器回复数据=" + replyData); } 客户端 NettyClient 改动如下 public static void main(String[] args) throws Exception {     // 初始化客户端事件组     EventLoopGroup group = new NioEventLoopGroup();     try {         Bootstrap b = new Bootstrap();         b.group(group)                 // 初始化通道                 .channel(NioSocketChannel.class)                 .option(ChannelOption.TCP_NODELAY, true)                 .handler(new ChannelInitializer() {                     // 初始化通道处理器                     @Override                     public void initChannel(SocketChannel ch) {                         ChannelPipeline p = ch.pipeline();                         // 使用分隔符"$_"的半包解码器                         ByteBuf byteBuf = Unpooled.copiedBuffer(DELIMITER.getBytes());                         ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, byteBuf));                         p.addLast(new NettyClientHandler());                     }                 });          b.connect(HOST, PORT).addListener(future -> {             log.info(String.format("连接服务器端:%s:%s 成功!", HOST, PORT));         }).await();     } catch (Exception e) {         log.error("启动客户端出现异常", e);     } } 客户端 NettyClientHandler 中接收数据的部分不变,发送数据的地方改动如下 // 重写 channelActive, 当客户端启动的时候 自动发送数据给服务端 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception {     // tcp 粘包现象     // 使用客户端分10次发送,每次数据量少     // tcp 会等客户端多次生产后,一次性进行发送     for (int i = 0; i < 10; i++) {         String data = "ckjava" + i;         log.info(String.format("客户端发送消息:[%s]", data));         ByteBuf buffer = Unpooled.copiedBuffer(data.concat(NettyClient.DELIMITER), StandardCharsets.UTF_8);         ctx.writeAndFlush(buffer);     } } 服务器端输出如下 18:14:33.627 [nioEventLoopGroup-2-1] INFO  com.ckjava.test.server.NettyServer - 服务器端启动成功,开放端口:8080 服务器收到的数据=ckjava0 服务器收到的数据次数=1 服务器回复数据=c6129b89-c869-4e06-97ca-55518c55aff7 服务器收到的数据=ckjava1 服务器收到的数据次数=2 服务器回复数据=bc3426cb-072f-4cb9-9f69-d2797863c9e4 服务器收到的数据=ckjava2 服务器收到的数据次数=3 服务器回复数据=43790702-1978-462b-a865-15c0ff2803af 服务器收到的数据=ckjava3 服务器收到的数据次数=4 服务器回复数据=4eb3e4e6-0c6a-4cef-a639-d6c40ebc27d2 服务器收到的数据=ckjava4 服务器收到的数据次数=5 服务器回复数据=6a9f02f9-9e0d-4eae-a380-605c3ba410d2 服务器收到的数据=ckjava5 服务器收到的数据次数=6 服务器回复数据=7ab9e20e-a86b-4f68-8673-5bc024643274 服务器收到的数据=ckjava6 服务器收到的数据次数=7 服务器回复数据=3b6b68cf-c066-4e32-8b5a-961c995fdd6d 服务器收到的数据=ckjava7 服务器收到的数据次数=8 服务器回复数据=cf2a5c51-96d9-4309-8f05-1c09abbe04f2 服务器收到的数据=ckjava8 服务器收到的数据次数=9 服务器回复数据=4d586684-be55-4c10-8071-a88dad5f0684 服务器收到的数据=ckjava9 服务器收到的数据次数=10 服务器回复数据=22fd511e-e65a-4f10-9426-f14b4524d4d0 客户端输出如下 18:14:50.056 [nioEventLoopGroup-2-1] INFO  com.ckjava.test.client.NettyClient - 连接服务器端:127.0.0.1:8080 成功! 18:14:50.058 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava0] 18:14:50.075 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava1] 18:14:50.076 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava2] 18:14:50.076 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava3] 18:14:50.076 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava4] 18:14:50.076 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava5] 18:14:50.076 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava6] 18:14:50.077 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava7] 18:14:50.077 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava8] 18:14:50.077 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端发送消息:[ckjava9] 18:14:50.104 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[c6129b89-c869-4e06-97ca-55518c55aff7] 18:14:50.105 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:1 18:14:50.105 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 18:14:50.105 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[bc3426cb-072f-4cb9-9f69-d2797863c9e4] 18:14:50.105 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:2 18:14:50.105 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[43790702-1978-462b-a865-15c0ff2803af] 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:3 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[4eb3e4e6-0c6a-4cef-a639-d6c40ebc27d2] 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:4 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[6a9f02f9-9e0d-4eae-a380-605c3ba410d2] 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:5 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[7ab9e20e-a86b-4f68-8673-5bc024643274] 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:6 18:14:50.106 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[3b6b68cf-c066-4e32-8b5a-961c995fdd6d] 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:7 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[cf2a5c51-96d9-4309-8f05-1c09abbe04f2] 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:8 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[4d586684-be55-4c10-8071-a88dad5f0684] 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:9 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息:[22fd511e-e65a-4f10-9426-f14b4524d4d0] 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - 客户端接收到消息的次数:10 18:14:50.107 [nioEventLoopGroup-2-1] INFO  c.c.test.client.NettyClientHandler - --------------------------------------------------- 从上面的例子可以看出 DelimiterBasedFrameDecoder 会帮自动帮我们把消息切割好,确保收到的数据都是基于 自定义分隔符 分隔好的数据, 但是不要忘记在发送数据的时候添加上 自定义分隔符 。

比肩戴森的国货宝藏吹风机,直白高速吹风机体验种草了就拔掉!最近,我入手了一款国货吹风机,直白的高速吹风机,也是很多明星同款,比如,欧阳娜娜吉克隽逸等,说出去都觉得档次不一样。经过我一番评测,一句话总结这款吹风机就是10万转高小米双11大放血,开启无套路补贴模式记得有一年,小米双十一搞了个无套路消费券活动,那一年的消费券没有任何要求,全品类都可以用,可谓是简单粗暴,而今年小米双十一搞了个更粗暴的活动,简答来说就是各种补贴,相比那年无套路消苹果iPadPro5G版或使用A14X芯片和新屏幕,机身重量再减轻目前苹果iPhone12新机频频被爆料,其产品线也开始陆陆续续有新消息,苹果每年都会有针对性对旗下产品某个系列进行更新,iPadMacBook也不例外。据国外媒体爆料,新款iPad小米获年轻人喜爱的品牌,人民网年轻人注重品质也更懂产品小米与年轻人的联系有多紧密,从最早期MIUI系统的百人内测,到后来的主打性价比的小米手机,再到如今各式各样的智能产品,可以说小米的产品受众群体一直是年轻人,而小米品牌也成了年轻人喜苹果会员比安卓贵?你是怎么想?前些日子,有个上海的消费者提起诉讼,该消费者说自己在苹果APPStore购买应用(爱奇艺喜马拉雅。网易云)这类APP的时候,发现这几款APP的的会员在苹果渠道的定价高于安卓的,而且iPhone用户好消息新版系统将推送,修复卡顿提升流畅稳定性!北京时间3月12日,苹果推送了iOS12。2开发者预览版和公测版Beta5系统更新,不出意外的话,这应该是苹果在3月25号春季发布会前最后一个测试版本,这次推送的iOS12。2的B郭明錤今年新款iPhone电池容量提升,加入三摄提升拍照!3月25号,苹果将召开春季新品发布会,届时发布新品或许有AirPods2iPad(iPadmini5)iPodTouch7iPhoneSE2Airpower无线充电板等。AirPo苹果两款iPad尺寸曝光10。2英寸和10。5英寸,低配2299元起!3月14日,有最新消息显示,苹果今年会发布两款iPad设备,一款是10。2英寸iPad7,另一款是10。5英寸iPad并且没有Pro版本,10。2英寸和10。5英寸不会在同一时间发小米华为发力5G手机高通和麒麟或许成为受益者,苹果今年无望!上个月在2019世界移动通信大会(MWC)上,小米展示了其5G手机型号为MIX3(5G版本),在发布会上,并且还打通了第一个5G国外视频电话,据了解这款新机价格定在599欧元(折合鸿蒙系统阵营,将会陆续加入其他国产手机品牌?只要关注了数码圈的人就都知道,华为的鸿蒙系统正式版将会在六月二号(也就是下个月)进行全面升级,这个消息让很多花粉都异常兴奋,而不止如此,笔者相信还有很多其他手机品牌的用户也都想体验这些设备或在iOS13推送升级范围,有你的吗?苹果一直以安全性和可靠性著称,最受欢迎就是其iOS生态,根据苹果一贯做法,下一个iOS版本(iOS13)升级将在2019年苹果全球开发者大会(AppleWWDC)期间宣布,此次大版
实力是根本,三星GalaxyZFold35G折叠屏手机解锁新体验冬去春来又是一年万物复苏的季节,此时此刻温度适宜空气微润,无疑是最适合和家人挚友一起踏青远足的好时节。春天是旅游的季节,和家人一起出游的时候会想要拿出手机拍照,记录下这美好的时光。Hinova9z正式亮相!支持66W快充,1799起值得选择吗?昨晚,中邮旗下新机Hinova9z正式发布,这款新机早在发布之前,就备受广大用户关注,那么其究竟都带来了哪些惊喜呢?首先在外观设计上,Hinova9z采用了矩阵式镜头模组设计,位于起售价不到2500元!媲美骁龙888性能的明智之选前言玉不琢,不成器,手机芯片不去打磨调校,就无法充分发挥其真正的实力。发布不久的一加Ace搭载的这颗天玑8100MAX定制芯片,就是一加与MTK联合深度调校之作。经过测试,它的强劲2000元以内,这4款低价大存储手机,谁才是真正的王者?便宜没好货,是很多人脑海中一贯的认知。这一竿子打死一船人的想法,让众多手机暗暗叫冤。任何事物都不能一概而论之,冷静而理智的人一直都喜欢用事实说话。手机中实实在在的数据配置,是骗不了国产黑马10秒突破1。2亿元,天玑810080W仅售2299元,K50不香了?声明原创不易,禁止搬运,违者必究!虽说现在的手机市场,有不少的手机品牌,苹果三星华为小米OV荣耀等等。但是在发展的过程中,其实不难发现,手机界也有自己的规律所在。对于国产手机来说,OPPOReno8Pro抓紧备货,4800mAh80W快充,摆脱高价低配束缚用火力全开来形容目前的国产手机市场一点没错,今年应该是处于5G换新的一个高峰期,所以手机厂商们看到5G换新的巨大需求,都在发布5G新机抢占市场。就目前来说,销量排名国内前二的viv出色感光能力OPPOK10Pro发售,OPPOK9沦为弃机,不买就亏大了搭载骁龙88880W超级闪充OPPOK10Pro这款产品的表现也再次刷新了我的认知。搭载骁龙88880W超级闪充OPPOK10Pro这款产品不仅用上了自家顶级旗舰的IMX766大底2022。01。26建议国家建设无人驾驶高速公路的想法2022。1。26的想法国家是否能够着手建设一条无人驾驶高速公路,货物无人驾驶高速公路上不能行驶载人车辆。公路可以建立在现有高速公路的旁边,这样货物无人驾驶高速公路和现有的高速公路不看真不知道,小米civi和红米k40哪个好,结局很给力?大家都知道,小米手机分小米系列和红米系列,这两个品牌本质上来说都是同一家公司的产品,只是各自面对消费群体不一样,最近有网友问我小米civi和红米k40哪个好?对于这个问题,如果单从手机不会截屏?教你8种方法,简单实用,只可惜全部了解的人太少今天和大家分享一下我们使用智能手机截屏和录屏的八种使用方法,操作呢也非常的简单,老年朋友也能够轻松的学会,这样我们就可以在手机上看到精彩内容的瞬间或者是有需要保留以及转发的聊天内容2021年全球可穿戴腕带设备出货量1。93亿台,苹果手表排名第一文福布斯中国市场调研机构Canalys最新数据显示,2021年全球可穿戴腕带设备出货量达到1。93亿台,同比增长4。3。具体到各大厂商来看,作为全球智能手表的领军者,苹果凭借着Ap