Unpacking and solution of Tcp sticking package

Keywords: Netty socket codec Java

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

Lack of years of study, knowledge is too shallow, please forgive me for saying wrong.

There are 10 kinds of people in the world, one is binary, the other is not binary.

78 original articles published, 54 praised, 460000 visitors+
Private letter follow

Posted by jaiswal on Wed, 12 Feb 2020 09:03:53 -0800