初识Netty之拆包和粘包

Netty 是由 JBOSS 提供的基于NIO一个 Java 开源网络编程框架。Netty 提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO 程序。作为当前最流行的 NIO 框架,Netty应用很广泛, hadoop、dubbo等底层RPC都是基于Netty实现的。以下是实现的章节:

TCP是个“流协议”,所谓流,就是没有界限的一串数据。就比如河里的流水,是连成一片的,其间没有分界线。在TCP的底层实现过程中,并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行划分,所有在业务上会认为,一个完整的包可能会被TCP拆分成多个包进行发送,也有可能把多个小包封装成一个大的数据包发送,这就是所谓的粘包和拆包。

TCP拆包和粘包问题说明:

关于拆包和粘包的问题,可以通过下图来充分说明:


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

  • (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)应用程序write写入的字节大小大于套接口发送缓冲区大小;
  • (2)进行MSS大小的TCP分段;
  • (3)以太网帧的payload大于MTU进行IP分片。 

粘包问题的解决策略:黏包的问题,其实解决思路在日期编码中也会涉及到,比如定长报文等策略。而在底层TCP由于无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,所有只能通过上层的应用协议栈设计来解决:可以归纳如下:

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

下面我们将上篇的入门案例改造下,来体现TCP的拆包,代码如下:

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;
/**
 * Netty服务类
 * @author Administrator
 *
 */
public class NettyServer {

	public static void main(String[] args) throws Exception {
		int port =9999;
		if(args!=null && args.length>0){
			port=Integer.valueOf(args[0]);
		}
		new NettyServer().bind(port);
		}
	
	public void bind(int port) throws Exception{
		//1.创建一个线程组:用来处理网络事件(接受客户端连接)
				EventLoopGroup bossGroup = new NioEventLoopGroup();
				//2.创建一个线程组:用来处理网络事件(处理通道 IO 操作)
				EventLoopGroup workerGroup = new NioEventLoopGroup();
				//3.创建服务器端启动助手来配置参数
				
				ServerBootstrap b = new ServerBootstrap();
				//使用 NioServerSocketChannel 作为服务器端通道实现
				b.group(bossGroup, workerGroup);
				b.channel(NioServerSocketChannel.class)//5.使用 NioServerSocketChannel 作为服务器端通道实现
					.option(ChannelOption.SO_BACKLOG, 1024)//6.设置线程队列中等待连接的个数
						.childOption(ChannelOption.SO_KEEPALIVE, true) //7.保持活动连接状态
							.childHandler(new ChannelInitializer<SocketChannel>() { //8.创建一个通道初始化对象
							public void initChannel(SocketChannel sc) {//9.往 Pipeline 链中添加自定义的业务处理 handle
						sc.pipeline().addLast(new NettyServerHandler()); //服务器端业务处理类
							}
					});
				//10.启动服务器端并绑定端口,等待接受客户端连接(非阻塞)
				ChannelFuture cf = b.bind(port).sync();
				System.out.println("......Server is Starting......");
				//11.关闭通道,关闭线程池
				cf.channel().closeFuture().sync();
				bossGroup.shutdownGracefully();
				workerGroup.shutdownGracefully();
		}
	}

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 NettyServerHandler extends  ChannelInboundHandlerAdapter {

	private int counter;
	@Override //读取数据事件
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		ByteBuf in = (ByteBuf) msg;
		byte []  req = new byte[in.readableBytes()];
		in.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.writeAndFlush(resp);
	}
	@Override //数据读取完毕事件
	public void channelReadComplete(ChannelHandlerContext ctx) {
		ctx.flush();
	}
	@Override //异常发生事件
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		cause.printStackTrace();
		ctx.close();
	}
}


/**
 * Netty客户端代码实现
 * @author Administrator
 *
 */
public class NettyClient {

	public static void main(String[] args) throws Exception {
		
		int port =9999;
		if(args!=null && args.length>0){
			port=Integer.valueOf(args[0]);
		}
		new NettyClient().connect(port, "127.0.0.1");
	}
	
	public void connect(int port,String host) throws Exception{
		//1.创建一个 EventLoopGroup 线程组
				EventLoopGroup group = new NioEventLoopGroup();
				//2.创建客户端启动助手
				Bootstrap b = new Bootstrap();
				b.group(group) //3.设置 EventLoopGroup 线程组
				.channel(NioSocketChannel.class) //4.使用 NioSocketChannel 作为客户端通道实现
				.handler(new ChannelInitializer<SocketChannel>() { //5.创建一个通道初始化对象
					@Override
					protected void initChannel(SocketChannel sc) { //6.往 Pipeline 链中添加自定义的业务处理 handler
						sc.pipeline().addLast(new NettyClientHandler()); //客户端业务处理类
						System.out.println("......Client is ready.......");
					}
				});
				//7.启动客户端,等待连接上服务器端(非阻塞)
				ChannelFuture cf = b.connect(host, port).sync();
				//8.等待连接关闭(非阻塞)
				cf.channel().closeFuture().sync();
	}
}


import com.sun.istack.internal.logging.Logger;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
 * 自定义客户端事件处理l
 * @author Administrator
 *
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter {

	private int counter;
	
	private static final Logger logger = Logger.getLogger(NettyClientHandler.class);
	
	private byte[] req;
	
	
	public NettyClientHandler(){
		req =("QUERY TIME ORDER"+System.getProperty("line.separator")).getBytes();
		
	}
	@Override //通道就绪事件
	public void channelActive(ChannelHandlerContext ctx) {
		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 in = (ByteBuf) msg;
		byte []  req = new byte[in.readableBytes()];
		in.readBytes(req);
		String body = new String(req,"UTF-8");
		System.out.println("Now is :" +body + ";the counter is :"+ ++counter);
	}
	@Override //数据读取完毕事件
	public void channelReadComplete(ChannelHandlerContext ctx) {
		
	}
	@Override //异常发生事件
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
		ctx.close();
	}
}

再来看下执行结果:

......Server is Starting......
The time server receive orderQUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORD;the counter is: 1
The time server receive order
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER
QUERY TIME ORDER;the counter is: 2

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

......Client is ready.......
Now is :BAD ORDER
BAD ORDER
;the counter is :1

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

解决粘包问题:

Netty框架中的编码器能有效解决TCP底层粘包的问题,而且使代码更加简洁:

优化后部分代码:

NettyServer
新增解码器
sc.pipeline().addLast(new LineBasedFrameDecoder(1024));//增加解码器
sc.pipeline().addLast(new StringDecoder());//增加解码器
public void bind(int port) throws Exception{
		//1.创建一个线程组:用来处理网络事件(接受客户端连接)
				EventLoopGroup bossGroup = new NioEventLoopGroup();
				//2.创建一个线程组:用来处理网络事件(处理通道 IO 操作)
				EventLoopGroup workerGroup = new NioEventLoopGroup();
				//3.创建服务器端启动助手来配置参数
				
				ServerBootstrap b = new ServerBootstrap();
				//使用 NioServerSocketChannel 作为服务器端通道实现
				b.group(bossGroup, workerGroup);
				b.channel(NioServerSocketChannel.class)//5.使用 NioServerSocketChannel 作为服务器端通道实现
					.option(ChannelOption.SO_BACKLOG, 1024)//6.设置线程队列中等待连接的个数
						.childOption(ChannelOption.SO_KEEPALIVE, true) //7.保持活动连接状态
							.childHandler(new ChannelInitializer<SocketChannel>() { //8.创建一个通道初始化对象
							public void initChannel(SocketChannel sc) {//9.往 Pipeline 链中添加自定义的业务处理 handle
								sc.pipeline().addLast(new LineBasedFrameDecoder(1024));//增加解码器
								sc.pipeline().addLast(new StringDecoder());//增加解码器
								sc.pipeline().addLast(new NettyServerHandler()); //服务器端业务处理类
							}
					});
				//10.启动服务器端并绑定端口,等待接受客户端连接(非阻塞)
				ChannelFuture cf = b.bind(port).sync();
				System.out.println("......Server is Starting......");
				//11.关闭通道,关闭线程池
				cf.channel().closeFuture().sync();
				bossGroup.shutdownGracefully();
				workerGroup.shutdownGracefully();
		}


NettyServerHandler
去掉解决半包问题的代码及对请求消息编码的部分
@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);
	}


NettyClient
与服务端修改的代码类似,增加了两个编码器
sc.pipeline().addLast(new LineBasedFrameDecoder(1024));//增加解码器
sc.pipeline().addLast(new StringDecoder());//增加解码器
public void connect(int port,String host) throws Exception{
		//1.创建一个 EventLoopGroup 线程组
				EventLoopGroup group = new NioEventLoopGroup();
				//2.创建客户端启动助手
				Bootstrap b = new Bootstrap();
				b.group(group) //3.设置 EventLoopGroup 线程组
				.channel(NioSocketChannel.class) //4.使用 NioSocketChannel 作为客户端通道实现
				.handler(new ChannelInitializer<SocketChannel>() { //5.创建一个通道初始化对象
					@Override
					protected void initChannel(SocketChannel sc) { //6.往 Pipeline 链中添加自定义的业务处理 handler
						sc.pipeline().addLast(new LineBasedFrameDecoder(1024));//增加解码器
						sc.pipeline().addLast(new StringDecoder());//增加解码器
						sc.pipeline().addLast(new NettyClientHandler()); //客户端业务处理类
						System.out.println("......Client is ready.......");
					}
				});
				//7.启动客户端,等待连接上服务器端(非阻塞)
				ChannelFuture cf = b.connect(host, port).sync();
				//8.等待连接关闭(非阻塞)
				cf.channel().closeFuture().sync();
	}



NettyClientHandler
msg已经是解码成字符串的之后的应答消息,不需要在进行转换
@Override //通道读取数据事件
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		String body = (String) msg;
		System.out.println("Now is :" +body + ";the counter is :"+ ++counter);
	}

代码执行结果:

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

 

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

 代码的执行结果与预期一致,解码器完美的解决了粘包的问题,那么LineBasedFrameDecoder和StringDecoder执行原理又是怎么样呢?

LineBasedFrameDecoder的工作原理是它依次遍历ByteBuf中的可读字节,判断看是否有“\n”或者“\r\n”,如果有,就以此位置为结束位置,从可读索引到结束位置区间的字节就组成了一行。它是以换行符为结束标志的解码器,支持携带结束符或者不携带结束符两种解码方式,同时支持配置单行的最大长度。如果连续读取到最大长度后仍然没有发现换行符,就会抛出异常,同时忽略掉之前读到的异常码流。
StringDecoder的功能非常简单,就是将接收到的对象转换成字符串,然后继续调用后面的handler。
LineBasedFrameDecoder+StringDecoder组合就是按行切换的文本解码器,它被设计用来支持TCP的粘包和拆包。

同时Netty不仅提供根据了换行符进行解码的解码器,同时还提供了针对消息长度、特殊分隔符、通过在消息头中定义长度字段来标识消息总裁长度的解码器,具体实现方法与换行符解码器类似,有兴趣的童鞋可以尝试下。

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值