Tcp stick package unpacking and Solutions

The viscosity Tcp packet unpacking

1.TCP is connection-oriented and stream-oriented, high reliability service, the receiver and transmitter, must have eleven pairs of socket, therefore, the transmitting end to the receiving end for a plurality of packets, a more effective hair to each other, using optimization methods (the Nagle algorithm), the gap is small and multiple small amount of data the data into one large block of data, then the packet, although doing so increases the efficiency, but it is difficult to distinguish the receiving end complete packet because the stream-oriented communication message protection is no boundary
2. because TCP message without protection border, border issues need to be addressed message at the receiving end, it is what we call the stick package, unpacking problem.

TCP stick package, illustrated unpacking

Here Insert Picture Description
Suppose the client sends two packets D1 D2 to the server, respectively, due to the number of bytes to read the service end time is uncertain, and therefore there may be four cases
1) is read twice to the server the two separate data packets, are not sticky D1 D2 package and unpacking
2) the server time received two packets D1 D2 bonded together into a stick package TCP
3) with the server data read twice packet, the first time a complete read packet D1 and D2 parts of the package, the second reading of the remaining contents of D2, which is called TCP unpacking
4) is read twice to the server data packet, the first part of a read packet D1_1 - D1, the second reading to the remaining portions of the package contents D1 D1_2 D2 and the full package.

TCP stick package and unpacking solutions

1. Use custom + codec protocol to resolve.
2. The key is to solve the problem of the server every time the length of the read data, to solve this problem, it will not read more or less primary server issues the read data, whereby avoid TCP stick package, unpacking.

Custom Tcp packet:
There are two properties, a length, a 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 codecs:
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方法被调用"+(++this.count)+"次");
        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 方法被调用"+(++this.count));
        // 将二进制字节码 -> messageProtocol
        int length = byteBuf.readInt();
        byte[] content = new byte[length];
        byteBuf.readBytes(content);
        // 分装成messageProtocol 给下一个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();
        }
    }
}

Initializer server 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("服务端接收信息如下");
        System.out.println("长度=" + len);
        System.out.println("内容=" + new String(bytes, Charset.forName("utf-8")));
        System.out.println("服务器接受消息包的数量" +(++this.count));

        // 回复消息
        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();
        }
    }
}

Initializer server 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;

    /**
     * 使用客户端发送10条数据,保温杯里泡枸杞
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for(int i = 0; i < 10; i++){
            String message = "保温杯里泡枸杞";
            byte[] bytes = message.getBytes(Charset.forName("utf-8"));
            int length = message.getBytes(Charset.forName("utf-8")).length;
            // 创建协议包
            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("从服务端接受到消息:");
        System.out.println("接收到的长度为:"+ messageProtocol.getLen());
        System.out.println("接收到的内容为:"+ new String(messageProtocol.getBytes(),Charset.forName("utf-8")));
    }
}

Mainly through custom data packets, so that each process a packet handler, the read data length to solve

Lack of study time, too shallow knowledge, that's wrong, please forgive me.

There are 10 kinds of people in the world, one is to understand binary, one is do not understand binary.

Published 78 original articles · won praise 54 · Views 460,000 +

Guess you like

Origin blog.csdn.net/weixin_43326401/article/details/104287539