Solve the stick package and unpacking issues
We know, TCP is a way of streams for network broadcast, when the three-way handshake tcp resume communications between the client server to establish a communication channel, we can imagine as a water pipe, the water flow out Citylink is one, there is no dividing line.
TCP does not know the specific meaning of the underlying upper layer service data, it will be divided based on the actual case of TCP packet buffer.
So for us the application layer. We intuitively transmit a continuous full TCP packet in at the bottom may occur to a complete split into a plurality of TCP packets or transmit a plurality of packets encapsulated into a large packet transmission.
This is called TCP stick package and unpacking.
3 | 0 When the TCP packet occurs sticky / unpacking what happens
We give a simple example illustrates:
The client sends to the server two packets: a first content 123; 456 second content. A data server to accept and make the appropriate business process (this is to accept print data add a comma).
Then the server output will appear the following four cases
Server response results | in conclusion |
---|---|
123,456, | Normal reception, and there is no occurrence of stick package unpacking |
123456, | Abnormal reception, tcp stick package occurs |
123,4,56, | Abnormal reception occurs tcp unpacking |
12,3456, | Abnormal reception, tcp occur stick package and unpacking |
4 | 0 How to solve
Mainstream protocol solutions can be summarized as follows:
- Message length, for example, each packet is a fixed size of 20 bytes, if not, gap fill space;
- Increase in trailer carriage return character cutting;
- Message into the message header and a message body, message header comprises the total length of the message field;
- More sophisticated application layer protocol.
For the case described earlier, where we can take Options 1 and 3.
Program-1 as an example: TCP packet every time we send only three numbers, then I will set three packets byte size, in which case, the server will be accepted in three-byte packet as a reference to this package unpacking station to solve the problem.
5 | 0 Netty solution Road
5|1LineBasedFrameDecoder
Ado directly on the code
Server
public class PrintServer {
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)); //1 arg0.pipeline().addLast(new StringDecoder()); //2 arg0.pipeline().addLast(new PrintServerHandler()); } } public static void main(String[] args) throws Exception { int port = 8080; new TimeServer().bind(port); } }
Handler server
public class PrintServerHandler extends ChannelHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); //将缓存区的字节数组复制到新建的req数组中 String body = new String(req, "UTF-8"); System.out.println(body); String response= "打印成功"; ByteBuf resp = Unpooled.copiedBuffer(response.getBytes()); ctx.write(resp); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { ctx.close(); } }
Client
public class PrintClient {
public void connect(int port, String host) 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<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( new LineBasedFrameDecoder(1024)); //3 ch.pipeline().addLast(new StringDecoder()); //4 ch.pipeline().addLast(new PrintClientHandler()); } }); ChannelFuture f = b.connect(host, port).sync(); f.channel().closeFuture().sync(); } finally { // 优雅退出,释放NIO线程组 group.shutdownGracefully(); } } /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { int port = 8080; new TimeClient().connect(port, "127.0.0.1"); } }
Client Handler
public class PrintClientHandler extends ChannelHandlerAdapter { private static final Logger logger = Logger .getLogger(TimeClientHandler.class.getName()); private final ByteBuf firstMessage; /** * Creates a client-side handler. */ public TimeClientHandler() { byte[] req = "你好服务端".getBytes(); firstMessage = Unpooled.buffer(req.length); firstMessage.writeBytes(req); } @Override public void channelActive(ChannelHandlerContext ctx) { ctx.writeAndFlush(firstMessage); } @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("服务端回应消息 : " + body); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 释放资源 System.out.println("Unexpected exception from downstream : " + cause.getMessage()); ctx.close(); } }
Appeals code logic in the previous chapter the same code logic, client-side data agreed to accept the service, and to respond to client information, print data after the client receives the data.
We observe the code can be found in order to solve the stick package unpacking Netty problem, just add the corresponding decoder in the preparation of the server and client pipeline to the appeal at 1,2,3,4 comment. The remaining code does not need to make any changes.
LineBasedFrameDecoder + StringDecoder combination is switched text row decoder, which is designed to support the TCP stick package and unpacking. Principle is: if he served the maximum length of continuous reading to discover natural no line breaks, it will throw an exception, while ignoring the abnormal stream prior supervision.
5|2DelimiteBasedFrameDecoder
The decoder can decode the message automatically to the end of stream delimiter as identified. (In fact, a similar decoder, if newline delimiter is specified, then the encoder acting on a substantially identical)
Use is also very simple:
Only you need to modify the server and client correspondence initChannel code to code
public void initChannel(SocketChannel ch) ByteBuf delimiter = Unpooled.copiedBuffer("_".getBytes()); //1 ch.pipeline().addLast( new DelimiterBasedFrameDecoder(1024, delimiter)); //2 ch.pipeline().addLast(new StringDecoder()); //3 ch.pipeline().addLast(new PrintHandler()); }
Note 1: First, create a separator buffer object ByteBuf, and specify "_" as the delimiter.
Note 2: The delimiter buffer objects ByteBuf incoming DelimiterBasedFrameDecoder, and specify the maximum length.
Note 3: string specified as a byte stream,
5|3FixedLengthFrameDecoder
The fixed-length decoder is a decoder which can decode detail automatically according to the specified length.
Use is also very simple:
Also only need to modify the server and client correspondence initChannel code to code
public void initChannel(SocketChannel ch)
throws Exception { ch.pipeline().addLast(new FixedLengthFrameDecoder(20)); ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new PrintHandler()); } });
So we specify, for each received stream of 20 bytes of the string of character sizes as a packet which will be processed through the line.
6 | 0 summary
Netty has done for us at the bottom of a lot of things, we simply need to use their decoder uses to provide good, I studied source content to be returned, and then expand, ha ha, finished live ~ sleep!