TCP sticking and unpacking

I. Overview

        TCP is connection-oriented, flow-oriented, and provides highly reliable services. Both the receiving and sending ends (client and server) must have a pair of sockets. Therefore, in order to send multiple packets to the receiving end more effectively, the sender uses an optimization method (Nagle algorithm). Combine multiple data with small intervals and small data volume into one large data block, and then packetize. Although this improves the efficiency, it is difficult for the receiving end to distinguish the complete data packet, because flow-oriented communication has no message protection boundary; because TCP has no message protection boundary, the message boundary problem needs to be dealt with at the receiving end, that is What we call the sticking and unpacking problems; when writing the Netty program, if there is no processing, there will be sticking and unpacking problems.

2. Detailed problem

Three, sticking and unpacking solutions

  1. Use custom protocol + codec to solve
  2. The key is to solve the problem of the length of data read by the server each time. If this problem is solved, the server will not read more or less data, thus avoiding TCP sticking and unpacking.

Four, code implementation

The client is required to send 5 Message objects, and the client sends one Message object each time

Each time the server receives a Message, it is decoded in 5 times. Each time a Message is read, a Message object will be returned to the client.

Client:

public class MyClient {
    public static void main(String[] args)  throws  Exception{

        EventLoopGroup group = new NioEventLoopGroup();

        try {

            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .handler(new MyClientInitializer()); //自定义一个初始化类

            ChannelFuture channelFuture = bootstrap.connect("localhost", 7000).sync();

            channelFuture.channel().closeFuture().sync();

        }finally {
            group.shutdownGracefully();
        }
    }
}

 

public class MyClientHandler extends SimpleChannelInboundHandler<MessageProtocol> { 

    private int count; 
    @Override 
    public void channelActive(ChannelHandlerContext ctx) throws Exception { 
        //Use the client to send 10 pieces of data "It’s cold today, eat hot pot" Number 

        for(int i = 0; i< 5; i++) { 
            String mes = "It's cold today, eat hot pot"; 
            byte[] content = mes.getBytes(Charset.forName("utf-8")); 
            int length = mes.getBytes(Charset.forName ("utf-8")).length; 

            //Create a protocol package object 
            MessageProtocol messageProtocol = new MessageProtocol(); 
            messageProtocol.setLen(length);  
            messageProtocol.setContent(content);
            ctx.writeAndFlush(messageProtocol);

        }

    }

// @Override 
    protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception { 

        int len ​​= msg.getLen(); 
        byte[] content = msg.getContent(); 

        System.out.println("The client received the following message "); 
        System.out.println("length=" + len); 
        System.out.println("content=" + new String(content, Charset.forName("utf-8"))); 

        System.out. println("The number of messages received by the client = "+ (++this.count)); 

    } 

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("异常消息=" + cause.getMessage());
        ctx.close(); 
    } 
}
public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {

        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new MyMessageEncoder()); //加入编码器
        pipeline.addLast(new MyMessageDecoder()); //加入解码器
        pipeline.addLast(new MyClientHandler());
    }
}

//Server

public class MyServer {
    public static void main(String[] args) throws Exception{

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {

            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new MyServerInitializer()); //自定义一个初始化类


            ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
            channelFuture.channel().closeFuture().sync();

        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }
}
//Handler that handles the business 
public class MyServerHandler extends SimpleChannelInboundHandler<MessageProtocol>{ 
    private int count; 

    @Override 
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 
        //cause.printStackTrace(); 
        ctx.close(); 
    } 

    @ Override 
    protected void channelRead0(ChannelHandlerContext ctx, MessageProtocol msg) throws Exception { 

        //Receive the data and process it 
        int len ​​= msg.getLen(); 
        byte[] content = msg.getContent(); 

        System.out.println(); 
        System.out.println(); 
        System.out.println(); 
        System.out.println("The server received the following information");
        System.out.println("length=" + len); 
        System.out.println("content=" + new String(content, Charset.forName("utf-8"))); 

        System.out.println(" The number of message packets received by the server=" + (++this.count)); 

        //Response message 

        String responseContent = UUID.randomUUID().toString(); 
        int responseLen = responseContent.getBytes("utf-8").length ; 
        byte[] responseContent2 = responseContent.getBytes("utf-8"); 
        //Build a protocol package 
        MessageProtocol messageProtocol = new MessageProtocol(); 
        messageProtocol.setLen(responseLen); 
        messageProtocol.setContent(responseContent2); 

        ctx.writeAndFlush(messageProtocol ); 


    } 
}
public class MyServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        pipeline.addLast(new MyMessageDecoder());//解码器
        pipeline.addLast(new MyMessageEncoder());//编码器
        pipeline.addLast(new MyServerHandler());
    }
}

 

//协议包
public class MessageProtocol {
    private int len; //关键
    private byte[] content;

    public int getLen() {
        return len;
    }

    public void setLen(int len) {
        this.len = len;
    }

    public byte[] getContent() {
        return content;
    }

    public void setContent(byte[] content) {
        this.content = content;
    }
}

//Codec

public class MyMessageEncoder extends MessageToByteEncoder<MessageProtocol> {
    @Override
    protected void encode(ChannelHandlerContext ctx, MessageProtocol msg, ByteBuf out) throws Exception {
        System.out.println("MyMessageEncoder encode 方法被调用");
        out.writeInt(msg.getLen());
        out.writeBytes(msg.getContent());
    }
}
public class MyMessageDecoder extends ReplayingDecoder<Void> { 
    @Override 
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { 
        System.out.println("MyMessageDecoder decode is called"); 
        //Need to get binary Bytecode -> MessageProtocol data packet (object) 
        int length = in.readInt(); 

        byte[] content = new byte[length]; 
        in.readBytes(content); 

        //encapsulate into MessageProtocol object, put it in out, and pass Next handler business processing 
        MessageProtocol messageProtocol = new MessageProtocol(); 
        messageProtocol.setLen(length); 
        messageProtocol.setContent(content); 

        out.add(messageProtocol); 

    } 
}

 

Guess you like

Origin blog.csdn.net/heijunwei/article/details/105621750