Sticking and unpacking of Tcp
1.TCP is connection oriented, flow oriented, and provides high reliability services. There must be one pair of socket s at both ends of the transceiver. Therefore, in order to send multiple packets to the receiver more effectively, the sender uses the optimization method (Nagle algorithm), In this way, although the efficiency is improved, it is difficult for the receiver to distinguish the complete data package, because the flow oriented communication has no message protection boundary
2. Because TCP has no message protection boundary, we need to deal with the problem of message boundary at the receiving end, that is to say, the problem of packet sticking and unpacking
TCP sticking and unpacking diagram
Suppose that the client sends two packets D1 D2 to the server. Since the number of bytes read by the server at one time is uncertain, there may be four situations as follows
1) The server reads two independent packets twice, namely, D1 and D2 are not stuck or unpacked
2) The server accepts two packets at a time. D1 and D2 are glued together to become TCP glued packets
3) The server reads the data packet with two times, the first time it reads the complete D1 packet and part of D2 packet, and the second time it reads the rest of D2 packet, which is called TCP unpacking
4) The server reads the data package twice, the first time it reads part of D1 package, the second time it reads the rest of D1 package and the complete D2 package
Solutions of sticking and unpacking TCP packets
1. Use custom protocol + codec to solve this problem
2. The key is to solve the problem of the length of data read by the server every time. If this problem is solved, the problem of more or less data read by the primary selection server will not be solved, so as to avoid TCP packet sticking and unpacking
Custom Tcp packets:
There are two properties, one length and one content
package com.jym.protocoltcp; /** * @program: NettyPro * @description: * @author: jym * @create: 2020/02/12 */ public class MessageProtocol { private int len; private byte[] bytes; public int getLen() { return len; } public void setLen(int len) { this.len = len; } public byte[] getBytes() { return bytes; } public void setBytes(byte[] bytes) { this.bytes = bytes; } }
Custom codec:
Encoder:
package com.jym.protocoltcp; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; /** * @program: NettyPro * @description: * @author: jym * @create: 2020/02/12 */ public class MessageEncoder extends MessageToByteEncoder<MessageProtocol> { private int count; @Override protected void encode(ChannelHandlerContext channelHandlerContext, MessageProtocol messageProtocol, ByteBuf byteBuf) throws Exception { System.out.println("MessageEncoder encode Method called"+(++this.count)+"second"); byteBuf.writeInt(messageProtocol.getLen()); byteBuf.writeBytes(messageProtocol.getBytes()); } }
Decoder:
package com.jym.protocoltcp; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ReplayingDecoder; import java.util.List; /** * @program: NettyPro * @description: * @author: jym * @create: 2020/02/12 */ public class MessageDecoder extends ReplayingDecoder<Void> { private int count; @Override protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception { System.out.println("MessageDecoder Method called"+(++this.count)); // Binary bytecode - > messageprotocol int length = byteBuf.readInt(); byte[] content = new byte[length]; byteBuf.readBytes(content); // Repackaged as message protocol to the next handler MessageProtocol messageProtocol = new MessageProtocol(); messageProtocol.setLen(length); messageProtocol.setBytes(content); list.add(messageProtocol); } }
Server:
package com.jym.protocoltcp; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * @program: NettyPro * @description: * @author: jym * @create: 2020/02/11 */ public class TcpServer { public static void main(String[] args) { EventLoopGroup boosGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(boosGroup,workerGroup).channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG,128) .childOption(ChannelOption.SO_KEEPALIVE,true) .childHandler(new TcpSeverInitializer()); ChannelFuture sync = serverBootstrap.bind(7000).sync(); sync.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { boosGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
Server Initializer and handler
package com.jym.protocoltcp; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; /** * @program: NettyPro * @description: * @author: jym * @create: 2020/02/12 */ public class TcpSeverInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new MessageDecoder()); pipeline.addLast(new MessageEncoder()); pipeline.addLast(new TcpSeverHandler()); } }
package com.jym.protocoltcp; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import java.nio.charset.Charset; import java.util.UUID; /** * @program: NettyPro * @description: * @author: jym * @create: 2020/02/12 */ public class TcpSeverHandler extends SimpleChannelInboundHandler<MessageProtocol> { private int count; @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageProtocol messageProtocol) throws Exception { int len = messageProtocol.getLen(); byte[] bytes = messageProtocol.getBytes(); System.out.println("The server receives the following information"); System.out.println("length=" + len); System.out.println("content=" + new String(bytes, Charset.forName("utf-8"))); System.out.println("Number of message packets accepted by the server" +(++this.count)); // Reply message String s = UUID.randomUUID().toString(); int length = s.getBytes("utf-8").length; MessageProtocol resp = new MessageProtocol(); resp.setLen(length); resp.setBytes(s.getBytes("utf-8")); channelHandlerContext.writeAndFlush(resp); } }
Client:
package com.jym.protocoltcp; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; /** * @program: NettyPro * @description: * @author: jym * @create: 2020/02/11 */ public class TcpClient { public static void main(String[] args) { NioEventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group).channel(NioSocketChannel.class) .handler(new TcpClientInitializer()); ChannelFuture sync = bootstrap.connect("127.0.0.1", 7000).sync(); sync.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } }
Server Initializer and handler
package com.jym.protocoltcp; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; /** * @program: NettyPro * @description: * @author: jym * @create: 2020/02/12 */ public class TcpClientInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new MessageEncoder()); pipeline.addLast(new MessageDecoder()); pipeline.addLast(new TcpClientHandler()); } }
package com.jym.protocoltcp; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import java.nio.charset.Charset; /** * @program: NettyPro * @description: * @author: jym * @create: 2020/02/12 */ public class TcpClientHandler extends SimpleChannelInboundHandler<MessageProtocol> { private int count; /** * Use the client to send 10 pieces of data and soak wolfberry in the thermos cup */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { for(int i = 0; i < 10; i++){ String message = "Chinese wolfberry in a thermos cup"; byte[] bytes = message.getBytes(Charset.forName("utf-8")); int length = message.getBytes(Charset.forName("utf-8")).length; // Create agreement package MessageProtocol messageProtocol = new MessageProtocol(); messageProtocol.setLen(length); messageProtocol.setBytes(bytes); ctx.writeAndFlush(messageProtocol); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageProtocol messageProtocol) throws Exception { System.out.println("Receive message from server:"); System.out.println("Received length is:"+ messageProtocol.getLen()); System.out.println("Received content is:"+ new String(messageProtocol.getBytes(),Charset.forName("utf-8"))); } }
It is mainly to let the handler process one data package at a time by customizing the data package, so as to solve the problem of reading the data length