你的位置:首页 > 软件开发 > Java > Netty(二)——TCP粘包/拆包

Netty(二)——TCP粘包/拆包

发布时间:2017-11-10 19:00:40
转载请注明出处  TCP是个“流”协议,所谓流,就是没有界限的一串数据。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓 ...

Netty(二)——TCP粘包/拆包

转载请注明出处  TCP是个“流”协议,所谓流,就是没有界限的一串数据。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包的问题。

1.1 TCP粘包/拆包问题说明

 我们通过下图对TCP粘包和拆包问题进行说明:

Netty(二)——TCP粘包/拆包

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

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

  如果此时服务端TCP接收滑窗非常小,而数据包D1和D2比较大,很可能会发生第五种可能,即服务端分多次才能将D1和D2包接收完全,期间发生多次拆包。

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

  问题产生的原因有三个:

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

1.3 粘包问题的解决策略

  由于底层的TCP无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案,可以归纳如下。

  1. 消息定长,例如每个报文的大小和固定长度200字节,如果不够,空位补空格;
  2. 在包尾增加回车换行符进行分割,例如FTP协议;
  3. 将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息体长度)的字段,通常设计思路为消息头的第一个字段使用int32来表示消息的总长度;
  4. 更复杂的应用层协议。

  下面我们就通过实际示例来看看如何使用Netty提供的半包解码器来解决TCP粘包/拆包问题。

2.未考虑TCP粘包导致功能异常案例

  在前面的时间服务器示例中,我们多次强调并没有考虑读半包问题,这在功能测试时往往没有问题,但是一旦压力上来,或者发送大报文之后,就会存在粘包/拆包问题。如果代码没有考虑,往往就会出现解码错位或者错误,导致程序不能正常工作。下面我们以前面的Netty(一)——Netty入门程序为例,模拟故障场景,然后看看如何正确使用Netty的半包解码器来解决TCP粘包/拆包问题。

2.1TimeServer的改造

package joanna.yan.netty;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;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;public class TimeServer {  public static void main(String[] args) throws Exception {  int port=9090;  if(args!=null&&args.length>0){   try {    port=Integer.valueOf(args[0]);   } catch (Exception e) {    // 采用默认值   }  }    new TimeServer().bind(port); }  public void bind(int port) throws Exception{  /*   * 配置服务端的NIO线程组,它包含了一组NIO线程,专门用于网络事件的处理,实际上它们就是Reactor线程组。   * 这里创建两个的原因:一个用于服务端接受客户端的连接,   * 另一个用于进行SocketChannel的网络读写。   */  EventLoopGroup bossGroup=new NioEventLoopGroup();  EventLoopGroup workerGroup=new NioEventLoopGroup();  try {   //ServerBootstrap对象,Netty用于启动NIO服务端的辅助启动类,目的是降低服务端的开发复杂度。   ServerBootstrap b=new ServerBootstrap();   b.group(bossGroup, workerGroup)    .channel(NioServerSocketChannel.class)    .option(ChannelOption.SO_BACKLOG, 1024)    /*    * 绑定I/O事件的处理类ChildChannelHandler,它的作用类似于Reactor模式中的handler类,    * 主要用于处理网络I/O事件,例如:记录日志、对消息进行编解码等。    */    .childHandler(new ChildChannelHandler());   /*    * 绑定端口,同步等待成功(调用它的bind方法绑定监听端口,随后,调用它的同步阻塞方法sync等待绑定操作完成。    * 完成之后Netty会返回一个ChannelFuture,它的功能类似于JDK的java.util.concurrent.Future,    * 主要用于异步操作的通知回调。)    */   ChannelFuture f=b.bind(port).sync();      //等待服务端监听端口关闭(使用f.channel().closeFuture().sync()方法进行阻塞,等待服务端链路关闭之后main函数才退出。)   f.channel().closeFuture().sync();  }finally{   //优雅退出,释放线程池资源   bossGroup.shutdownGracefully();   workerGroup.shutdownGracefully();  } }  private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{  @Override  protected void initChannel(SocketChannel arg0) throws Exception {//   arg0.pipeline().addLast(new TimeServerHandler());      //模拟粘包/拆包故障场景   arg0.pipeline().addLast(new TimeServerHandler1());  } }}
import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;/** * 用于对网络事件进行读写操作 * 模拟粘包/拆包故障场景 * @author Joanna.Yan * @date 2017年11月8日下午6:54:35 */public class TimeServerHandler1 extends ChannelInboundHandlerAdapter{  private int counter; @Override public void channelRead(ChannelHandlerContext ctx, Object msg)   throws Exception {  ByteBuf buf=(ByteBuf) msg;  byte[] req=new byte[buf.readableBytes()];  buf.readBytes(req);  String body=new String(req, "UTF-8").substring(0, req.length-System.getProperty("line.separator").length());  System.out.println("The time server receive order : "+body+" ;the counter is :"+ ++counter);  String currentTime="QUERY TIME ORDER".equalsIgnoreCase(body) ? new     Date(System.currentTimeMillis()).toString() : "BAD ORDER";  currentTime=currentTime+System.getProperty("line.separator");    ByteBuf resp=Unpooled.copiedBuffer(currentTime.getBytes());  ctx.write(resp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)   throws Exception {  ctx.close(); }  }

2.2TimeClient的改造

package joanna.yan.netty;import io.netty.bootstrap.Bootstrap;import io.netty.channel.ChannelFuture;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.NioSocketChannel;public class TimeClient { public static void main(String[] args) throws Exception {  int port=9090;  if(args!=null&&args.length>0){   try {    port=Integer.valueOf(args[0]);   } catch (Exception e) {    // 采用默认值   }  }  new TimeClient().connect(port, "127.0.0.1"); }  public void connect(int port,String host) throws Exception{  //配置客户端NIO线程组  EventLoopGroup group=new NioEventLoopGroup();    try {   Bootstrap b=new Bootstrap();   b.group(group)   .channel(NioSocketChannel.class)   .option(ChannelOption.TCP_NODELAY, true)   .handler(new ChannelInitializer<SocketChannel>() {    @Override    protected void initChannel(SocketChannel ch) throws Exception {//     ch.pipeline().addLast(new TimeClientHandler());          //模拟粘包/拆包故障场景     ch.pipeline().addLast(new TimeClientHandler1());    }   });   //发起异步连接操作   ChannelFuture f=b.connect(host, port).sync();   //等待客户端链路关闭   f.channel().closeFuture().sync();  }finally{   //优雅退出,释放NIO线程组   group.shutdownGracefully();  } }}
package joanna.yan.netty;import java.util.logging.Logger;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;/** * 模拟粘包/拆包故障场景 * @author Joanna.Yan * @date 2017年11月10日下午2:18:51 */public class TimeClientHandler1 extends ChannelInboundHandlerAdapter{  private static final Logger logger=Logger.getLogger(TimeClientHandler1.class.getName()); private int counter; private byte[] req;  public TimeClientHandler1(){  req=("QUER TIME ORDER"+System.getProperty("line.separator")).getBytes(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception {  ByteBuf message=null;  for (int i = 0; i < 100; i++) {   message=Unpooled.buffer(req.length);   message.writeBytes(req);   ctx.writeAndFlush(message);  } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg)   throws Exception {  ByteBuf buf=(ByteBuf) msg;  byte[] req=new byte[buf.readableBytes()];  buf.readBytes(req);  String body=new String(req, "UTF-8");  System.out.println("Now is :"+body+" ;the counter is :" + ++counter); }  @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)   throws Exception {  //释放资源  logger.warning("Unexpected exception from downstream : "+cause.getMessage());  ctx.close(); }}

2.3运行结果

  分别执行服务端和客户端,运行结果如下:

  服务端运行结果如下:

Netty(二)——TCP粘包/拆包Netty(二)——TCP粘包/拆包
The time server receive order : QUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQU ;the counter is :1The time server receive order : TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDERQUER TIME ORDER ;the counter is :2
View Code

  服务端运行结果表明它只接收到了两条消息,总数正好是100条,我们期待的是收到100条消息,每条包括一条“QUERY TIME ORDER”指令。这说明发生了TCP粘包。

  客户端运行结果如下:

Now is : BAD ORDERBAD ORDER; the counter is : 1

  按照设计初衷,客户端应该收到100条当前系统时间的消息,但实际上只收到了一条。这不难理解,因为服务端只收到了2条请求消息,所以实际服务端只发送了2条应答,由于请求消息不满足查询条件,所以只返回了2条“BAD ORDER”应答消息。但是实际上客户端只收到了一条包含2条“BAD ORDER”指令的消息,说明服务端返回的应答消息也发送了粘包。

  由于上面的例程没有考虑TCP的粘包/拆包,所以当发生TCP粘包时,我们的程序就不能正常工作。

  下面我们通过Netty的LineBaseFrameDecoder和StringDecoder来解决TCP粘包问题。

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

  为了解决TCP粘包/拆包导致的半包读写问题,Netty默认提供了多种编解码器用于处理半包,只要能熟练掌握这些类库的使用,TCP粘包问题从此会变得非常容易,你甚至不需要关心它们,这也是其他NIO框架和JDK原生的NIO API所无法匹敌的。

  下面我们对时间服务进行修改。

3.1支持TCP粘包的TimeServer

package joanna.yan.netty.sp;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;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 io.netty.handler.codec.LineBasedFrameDecoder;import io.netty.handler.codec.string.StringDecoder;public class TimeServer { public static void main(String[] args) throws Exception {  int port=9090;  if(args!=null&&args.length>0){   try {    port=Integer.valueOf(args[0]);   } catch (Exception e) {    // 采用默认值   }  }  new TimeServer().bind(port); }  public void bind(int port) throws Exception{  //配置服务端的NIO线程组  EventLoopGroup bossGroup=new NioEventLoopGroup();  EventLoopGroup workerGroup=new NioEventLoopGroup();  try {   ServerBootstrap b=new ServerBootstrap();   b.group(bossGroup, workerGroup)    .channel(NioServerSocketChannel.class)    .option(ChannelOption.SO_BACKLOG, 1024)    .childHandler(new ChildChannelHandler());      //绑定端口,同步等待成功   ChannelFuture f=b.bind(port).sync();      //等待服务端监听端口关闭   f.channel().closeFuture().sync();     }finally{   //优雅退出,释放线程池资源   bossGroup.shutdownGracefully();   workerGroup.shutdownGracefully();  } }  private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{  @Override  protected void initChannel(SocketChannel arg0) throws Exception {   arg0.pipeline().addLast(new LineBasedFrameDecoder(1024));   arg0.pipeline().addLast(new StringDecoder());   arg0.pipeline().addLast(new TimeServerHandler());  }  }}
package joanna.yan.netty.sp;import java.util.Date;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;public class TimeServerHandler extends ChannelInboundHandlerAdapter{ private int counter; @Override public void channelRead(ChannelHandlerContext ctx, Object msg)   throws Exception {  String body=(String) msg;  System.out.println("The time server receive order : "+body+" ;the counter is : "+ ++counter);  String currentTime="QUERY TIME ORDER".equalsIgnoreCase(body) ? new     Date(System.currentTimeMillis()).toString() : "BAD ORDER";  currentTime =currentTime+System.getProperty("line.separator");   ByteBuf resp=Unpooled.copiedBuffer(currentTime.getBytes());  ctx.writeAndFlush(resp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)   throws Exception {  ctx.close(); }}

3.2支持TCP粘包的TimeClient

package joanna.yan.netty.sp;import io.netty.bootstrap.Bootstrap;import io.netty.channel.ChannelFuture;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.NioSocketChannel;import io.netty.handler.codec.LineBasedFrameDecoder;import io.netty.handler.codec.string.StringDecoder;public class TimeClient {  public static void main(String[] args) throws Exception {  int port=9090;  if(args!=null&&args.length>0){   try {    port=Integer.valueOf(args[0]);   } catch (Exception e) {    // 采用默认值   }  }  new TimeClient().connect(port, "127.0.0.1"); }  public void connect(int port,String host) throws Exception{  //配置客户端NIO线程组  EventLoopGroup group=new NioEventLoopGroup();    try {   Bootstrap b=new Bootstrap();   b.group(group)   .channel(NioSocketChannel.class)   .option(ChannelOption.TCP_NODELAY, true)   .handler(new ChannelInitializer<SocketChannel>() {    @Override    protected void initChannel(SocketChannel ch) throws Exception {     //直接在TimeClientHandler之前新增LineBasedFrameDecoder和StringDecoder解码器     ch.pipeline().addLast(new LineBasedFrameDecoder(1024));     ch.pipeline().addLast(new StringDecoder());     ch.pipeline().addLast(new TimeClientHandler());    }   });      //发起异步连接操作   ChannelFuture f=b.connect(host, port).sync();   //等待客户端链路关闭   f.channel().closeFuture().sync();  }finally{   //优雅退出,释放NIO线程组   group.shutdownGracefully();  } }}
package joanna.yan.netty.sp;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import java.util.logging.Logger;public class TimeClientHandler extends ChannelInboundHandlerAdapter{  private static final Logger logger=Logger.getLogger(TimeClientHandler.class.getName()); private int counter; private byte[] req;  public TimeClientHandler(){  req=("QUERY TIME ORDER"+System.getProperty("line.separator")).getBytes(); }  @Override public void channelActive(ChannelHandlerContext ctx) throws Exception {  ByteBuf message=null;  for (int i = 0; i < 100; i++) {   message=Unpooled.buffer(req.length);   message.writeBytes(req);   ctx.writeAndFlush(message);  } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg)   throws Exception {  //拿到的msg已经是解码成字符串之后的应答消息了,相比于之前的代码简洁明了很多。  String body=(String) msg;  System.out.println("Now is :"+body+" ;the counter is :" + ++counter); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)   throws Exception {  //释放资源  logger.warning("Unexpected exception from downstream : "+cause.getMessage());  ctx.close(); }}

3.3运行支持TCP粘包的时间服务器程序

  为了尽量模拟TCP粘包和半包场景,采用简单的压力测试,链路建立成功之后,客户端连续发送100条消息给服务端,然后查看服务端和客户端的运行结果。

  服务端执行结果如下:

Netty(二)——TCP粘包/拆包Netty(二)——TCP粘包/拆包
The time server receive order : QUER TIME ORDER ;the counter is : 1The time server receive order : QUER TIME ORDER ;the counter is : 2The time server receive order : QUER TIME ORDER ;the counter is : 3The time server receive order : QUER TIME ORDER ;the counter is : 4The time server receive order : QUER TIME ORDER ;the counter is : 5The time server receive order : QUER TIME ORDER ;the counter is : 6The time server receive order : QUER TIME ORDER ;the counter is : 7The time server receive order : QUER TIME ORDER ;the counter is : 8The time server receive order : QUER TIME ORDER ;the counter is : 9The time server receive order : QUER TIME ORDER ;the counter is : 10The time server receive order : QUER TIME ORDER ;the counter is : 11The time server receive order : QUER TIME ORDER ;the counter is : 12The time server receive order : QUER TIME ORDER ;the counter is : 13The time server receive order : QUER TIME ORDER ;the counter is : 14The time server receive order : QUER TIME ORDER ;the counter is : 15The time server receive order : QUER TIME ORDER ;the counter is : 16The time server receive order : QUER TIME ORDER ;the counter is : 17The time server receive order : QUER TIME ORDER ;the counter is : 18The time server receive order : QUER TIME ORDER ;the counter is : 19The time server receive order : QUER TIME ORDER ;the counter is : 20The time server receive order : QUER TIME ORDER ;the counter is : 21The time server receive order : QUER TIME ORDER ;the counter is : 22The time server receive order : QUER TIME ORDER ;the counter is : 23The time server receive order : QUER TIME ORDER ;the counter is : 24The time server receive order : QUER TIME ORDER ;the counter is : 25The time server receive order : QUER TIME ORDER ;the counter is : 26The time server receive order : QUER TIME ORDER ;the counter is : 27The time server receive order : QUER TIME ORDER ;the counter is : 28The time server receive order : QUER TIME ORDER ;the counter is : 29The time server receive order : QUER TIME ORDER ;the counter is : 30The time server receive order : QUER TIME ORDER ;the counter is : 31The time server receive order : QUER TIME ORDER ;the counter is : 32The time server receive order : QUER TIME ORDER ;the counter is : 33The time server receive order : QUER TIME ORDER ;the counter is : 34The time server receive order : QUER TIME ORDER ;the counter is : 35The time server receive order : QUER TIME ORDER ;the counter is : 36The time server receive order : QUER TIME ORDER ;the counter is : 37The time server receive order : QUER TIME ORDER ;the counter is : 38The time server receive order : QUER TIME ORDER ;the counter is : 39The time server receive order : QUER TIME ORDER ;the counter is : 40The time server receive order : QUER TIME ORDER ;the counter is : 41The time server receive order : QUER TIME ORDER ;the counter is : 42The time server receive order : QUER TIME ORDER ;the counter is : 43The time server receive order : QUER TIME ORDER ;the counter is : 44The time server receive order : QUER TIME ORDER ;the counter is : 45The time server receive order : QUER TIME ORDER ;the counter is : 46The time server receive order : QUER TIME ORDER ;the counter is : 47The time server receive order : QUER TIME ORDER ;the counter is : 48The time server receive order : QUER TIME ORDER ;the counter is : 49The time server receive order : QUER TIME ORDER ;the counter is : 50The time server receive order : QUER TIME ORDER ;the counter is : 51The time server receive order : QUER TIME ORDER ;the counter is : 52The time server receive order : QUER TIME ORDER ;the counter is : 53The time server receive order : QUER TIME ORDER ;the counter is : 54The time server receive order : QUER TIME ORDER ;the counter is : 55The time server receive order : QUER TIME ORDER ;the counter is : 56The time server receive order : QUER TIME ORDER ;the counter is : 57The time server receive order : QUER TIME ORDER ;the counter is : 58The time server receive order : QUER TIME ORDER ;the counter is : 59The time server receive order : QUER TIME ORDER ;the counter is : 60The time server receive order : QUER TIME ORDER ;the counter is : 61The time server receive order : QUER TIME ORDER ;the counter is : 62The time server receive order : QUER TIME ORDER ;the counter is : 63The time server receive order : QUER TIME ORDER ;the counter is : 64The time server receive order : QUER TIME ORDER ;the counter is : 65The time server receive order : QUER TIME ORDER ;the counter is : 66The time server receive order : QUER TIME ORDER ;the counter is : 67The time server receive order : QUER TIME ORDER ;the counter is : 68The time server receive order : QUER TIME ORDER ;the counter is : 69The time server receive order : QUER TIME ORDER ;the counter is : 70The time server receive order : QUER TIME ORDER ;the counter is : 71The time server receive order : QUER TIME ORDER ;the counter is : 72The time server receive order : QUER TIME ORDER ;the counter is : 73The time server receive order : QUER TIME ORDER ;the counter is : 74The time server receive order : QUER TIME ORDER ;the counter is : 75The time server receive order : QUER TIME ORDER ;the counter is : 76The time server receive order : QUER TIME ORDER ;the counter is : 77The time server receive order : QUER TIME ORDER ;the counter is : 78The time server receive order : QUER TIME ORDER ;the counter is : 79The time server receive order : QUER TIME ORDER ;the counter is : 80The time server receive order : QUER TIME ORDER ;the counter is : 81The time server receive order : QUER TIME ORDER ;the counter is : 82The time server receive order : QUER TIME ORDER ;the counter is : 83The time server receive order : QUER TIME ORDER ;the counter is : 84The time server receive order : QUER TIME ORDER ;the counter is : 85The time server receive order : QUER TIME ORDER ;the counter is : 86The time server receive order : QUER TIME ORDER ;the counter is : 87The time server receive order : QUER TIME ORDER ;the counter is : 88The time server receive order : QUER TIME ORDER ;the counter is : 89The time server receive order : QUER TIME ORDER ;the counter is : 90The time server receive order : QUER TIME ORDER ;the counter is : 91The time server receive order : QUER TIME ORDER ;the counter is : 92The time server receive order : QUER TIME ORDER ;the counter is : 93The time server receive order : QUER TIME ORDER ;the counter is : 94The time server receive order : QUER TIME ORDER ;the counter is : 95The time server receive order : QUER TIME ORDER ;the counter is : 96The time server receive order : QUER TIME ORDER ;the counter is : 97The time server receive order : QUER TIME ORDER ;the counter is : 98The time server receive order : QUER TIME ORDER ;the counter is : 99The time server receive order : QUER TIME ORDER ;the counter is : 100
View Code

  客户端执行结果如下:

Netty(二)——TCP粘包/拆包Netty(二)——TCP粘包/拆包
Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :1Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :2Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :3Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :4Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :5Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :6Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :7Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :8Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :9Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :10Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :11Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :12Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :13Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :14Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :15Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :16Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :17Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :18Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :19Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :20Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :21Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :22Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :23Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :24Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :25Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :26Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :27Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :28Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :29Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :30Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :31Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :32Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :33Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :34Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :35Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :36Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :37Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :38Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :39Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :40Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :41Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :42Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :43Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :44Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :45Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :46Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :47Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :48Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :49Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :50Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :51Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :52Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :53Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :54Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :55Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :56Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :57Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :58Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :59Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :60Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :61Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :62Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :63Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :64Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :65Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :66Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :67Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :68Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :69Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :70Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :71Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :72Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :73Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :74Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :75Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :76Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :77Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :78Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :79Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :80Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :81Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :82Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :83Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :84Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :85Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :86Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :87Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :88Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :89Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :90Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :91Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :92Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :93Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :94Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :95Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :96Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :97Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :98Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :99Now is :Wed Aug 30 16:38:34 CST 2017 ;the counter is :100
View Code

  程序的运行结果完全符合预期,说明通过使用LineBasedFrameDecoder和StringDecoder成功解决了TCP粘包导致的读半包问题。对于使用者来说,只要将支持半包解码的handler添加到ChannelPipeline中即可,不需要写额外的代码,用户使用起来非常简单。

3.4 LineBasedFrameDecoder和StringDecoder原理分析

  LineBasedFrameDecoder的工作原理是它依次遍历ByteBuf中的可读字节,判断看是否有"\n"或者“\r\n”,如果有,就以此位置为结束位置,从可读索引到结束位置区间的字节就组成了一行。它是以换行符为结束标志的解码器,支持携带结束符或者不携带结束符两种解码方式。同时支持配置单行的最大长度。如果连续读取到的最大长度后仍没有发现换行符,就会抛出异常,同时忽略掉之前督导的异常码流。

  StringDecoder的功能非常简单,就是将收到到的对象转换成字符串,然后继续调用后面的handler。LineBasedFrameDecoder+StringDecoder组合就是按行切换的文本解码器,它被设计用来支持TCP的粘包和拆包。

  疑问:如果发送的消息不是以换行符结束的该怎么办呢?或者没有回车换行符,靠消息头中的长度字段来分包怎么办?是不是需要自己写半包解码器?答案是否定的,Netty提供了多种支持TCP粘包/拆包的解码器,用来满足用户的不同诉求。

如果此文对您有帮助,微信打赏我一下吧~

Netty(二)——TCP粘包/拆包

 

 

海外公司注册、海外银行开户、跨境平台代入驻、VAT、EPR等知识和在线办理:https://www.xlkjsw.com

原标题:Netty(二)——TCP粘包/拆包

关键词:net

net
*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。