In the last article is mainly the use of Springboot to develop a basic case of Netty communication, this article is on the basis of the last article to explain, mainly considering how to solve the problem of sticky packet transmission data.

This article will follow the steps to explain, hope you gain:

1. What is TCP sticky packet unpacking

2. Sticky packets in Netty reappear

3, Netty sticky package problem solution

OK, with this basic framework in mind you can start today’s article. All articles in this series give the complete code and run it on your computer to make sure it’s correct.

What is TCP unpacking and sticky packet

When we use TCP protocol to transfer data, if the data block is large, we will consider to slice it. To cut a large packet into smaller packets and send them. At this time will encounter unpacking and sticky package problems.

For example, if the client sends two packets D1 and D2 to the server, the following problems may occur during transmission:

I think you get the idea by looking at the picture above. But we need to explain a little bit here:

Case 1: D1 and D2 are sent normally, one whole packet at a time.

Case 2: THE D1 packet is large and the D2 packet is small. The first time sends part of D1, and the second time sends the rest of D1 and the whole packet of D2. It’s called unpacking.

Case 2: Both D1 and D2 packets are small, and two whole packets are sent at once, which is called sticky packets.

Case 4: THE D1 packet is small and the D2 packet is large. The first time sends D1 whole packet and part of D2, and the second time sends the rest of D2. It’s called unpacking.

Case 5: Both D1 and D2 packets are large, so they are developed separately.

Why does this happen? TCP has a sliding window to control the size of the data it receives. This sliding window can be interpreted as the size of a buffer. The data is sent when the buffer is full. The size of the packet is not fixed, sometimes larger than the buffer, sometimes smaller. This is where the above phenomenon occurs.

Let’s use code to reproduce this phenomenon.

Second, problems recur

1. Preparation

We are developing based on Springboot, so as in the previous section, we will first create a Springboot Web project and add the following dependencies:

<! -- Add netty dependency -->
<dependency>
      <groupId>io.netty</groupId>
      <artifactId>netty-all</artifactId>
      <version>5.0.0. 1</version>
</dependency>
Copy the code

If you don’t use Maven, download the jars and import them directly into your IDE.

2. Server-side code development

Step 1: Create the Server class

This Server class, mentioned in the previous article, is a template class that you can use directly.

@Component
public class NettyServer {
    EventLoopGroup boss = new NioEventLoopGroup();
    EventLoopGroup work = new NioEventLoopGroup();
    @PostConstruct
    public void start(a) throws InterruptedException {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, work)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        public void initChannel(SocketChannel sc) throws Exception {
                            sc.pipeline().addLast(newServerUAVHandler()); }}); ChannelFuture cf = bootstrap.bind(8881).sync();
            System.out.println("Startup successful");
    }
    @PreDestroy
    private void destory(a) throws Exception{ boss.shutdownGracefully(); work.shutdownGracefully(); }}Copy the code

In the above code we also focus on the ServerUAVHandler implementation.

Step 2: Implementation of Handler

public class ServerUAVHandler extends ChannelHandlerAdapter {
    private int counter=0;
    @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("Client information is:"+body);
        System.out.println(body+",counter:"+ ++counter);
        ByteBuf resp=Unpooled.copiedBuffer(("A"
               +System.getProperty("line.separator")).getBytes()); ctx.writeAndFlush(resp); }}Copy the code

In this class, the channelRead method is used to read the information sent by the client.

(1) First we define a counter to count how many messages the client has sent.

(2) Inside channelRead, MSG is first converted to ByteBuf.

(3) Convert buF data into byte

(4) Convert buF byte data to String and output it.

(5) Use CTX’s writeAndFlush method to reply each client with an A. Don’t forget the newline character.

In the above code, the main thing is that the server responds to each message it receives from the client. This means that the number of messages on the client and server should be the same.

3. Client code development

Step 1: Create the Client class

@Component
public class NettyClient {
    private SocketChannel socketChannel;
    EventLoopGroup group = new NioEventLoopGroup();
    @PostConstruct
    public void start(a) throws Exception {
        try{
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .option(ChannelOption.SO_KEEPALIVE,true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(newClientHandler()); }}); ChannelFuture cf = b.connect("127.0.0.1".8881).sync();
            System.out.println("Link successful");
            cf.channel().closeFuture().sync();
        }finally{ group.shutdownGracefully(); }}}Copy the code

The same code logic was explained in the previous article, but we’ll focus on the event handling class Handler.

Step 2: Handler implementation

public class ClientHandler extends ChannelHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ByteBuf resp = null;
        for (int i=0; i<100; i++){ resp=Unpooled.copiedBuffer(("I love you."
                +System.getProperty("line.separator")).getBytes()); ctx.writeAndFlush(resp); }}@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+""); }}Copy the code

The Handler for this client may seem a bit numerous, with two methods, channelActive and channelRead.

(1) channelActive sends 100 “I love you” messages to the server using the for loop. A newline character is added to the end of each send.

(2) channelRead accepts messages returned by the server.

Logically, if the client sends 100 pieces of data to the server, the server will return 100 pieces. So let’s verify that.

This output is the server side of the message, from the above output results you will find that the client side of the “I love you” is stuck together. There were 100 pieces but now there are only 17 pieces, which is the phenomenon of sticky bags.

How to solve it? So let’s see.

3. Solve the sticky bag problem

The solution is very simple, that is, each time a packet is sent, add an identifier, read until this identifier represents a complete packet. To this we add line. Separator, or “\n”.

1. The server class is changed.

2. Server Handler class changes

3. Change the Client

4. Client Handler changes

The client side and the server side changed the same place, but still posted, now let’s run another wave.

See, it’s amazing. Let’s analyze what we’ve changed.

It looks like we just added two classes to the server and client classes. One is LineBasedFrameDecoder and one is StringDecoder. The other classes are deleted directly.

(1) LineBasedFrameDecoder is used to read data until it contains a newline character “\n” or “\r\n”. If you do, it means it’s over. So you get this row of packets.

(2) StringDecoder is used to decode the line of data that was read by LineBasedFrameDecoder. Converts an object to a string.

Now we can solve the sticky packet problem, but we also have a new problem, that is, what if our identifier is not a newline “\n” or “\r\n”? Fortunately, Netty also provides us with several other codecs, called DelimiterBasedFrameDecoder and FixedLengthFrameDecoder, the front end of this can be done automatically with a delimiter do sign, behind this can be done automatically decoding of fixed-length message. Can solve the problem of sticky package unpacking.

Now we’ve solved one big problem, but we’ve also got a big new problem, which is that all of our data is transmitted over the network, so we need to encode and decode. What encoding and decoding technology does Netty provide for us? The next article is even better. Thank you.