使用Netty开发,踩坑到解决全过程(附解决方案源码和Netty系列)

在工作中用到Netty进行服务端开发,当服务端接收上位机字节数超过1000字节,服务端接收数据错误。为 什么服务端接收数据有时候没有问题,有时候会接收数据混乱。在不断的测试和对Netty知识点巩固,Netty 在项目开发中有了新的思考。

在TCP通信一定会出现粘包、拆包问题?如果不一定,什么时候出现粘包拆包问题?如果服务端单次接收字节 数超过1000字节的时候,byteBuf容量小于1000字节会出现什么问题?如何解决?

备注(Netty系列)

如果想要对网络编程和Netty是个小白,建议查看列表三篇文章。
1 七层协议和TCP/IP协议、三次握手四次挥手、BIO、NIO(Netty前置)
2 一文入门Netty(Netty一)
3 项目开发中如何选择编解码器?如何解决TCP粘包问题?(Netty二)

本文解决方案代码
GitHub代码地址
gitee代码地址

一 粘包(服务端客户端之间通信)

1.1 实例不出现粘包

1.1.1 代码实现

客户端以每500毫秒间隔向服务端发送相同字节数组,服务端将接收到字节转化为字符串,打印出来。
1. 服务端的配置类

public class TcpServer {
    private final int port;

    public TcpServer(int port) {
        this.port = port;
    }

    public void init() {
        NioEventLoopGroup boss = new NioEventLoopGroup();//主线程组
        NioEventLoopGroup work = new NioEventLoopGroup();//工作线程组
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();//引导对象
            bootstrap.group(boss, work);//配置工作线程组
            bootstrap.channel(NioServerSocketChannel.class);//配置为NIO的socket通道
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                protected void initChannel(SocketChannel ch) throws Exception {//绑定通道参数
                    ch.pipeline().addLast("logging", new LoggingHandler("DEBUG"));//设置log监听器,并且日志级别为debug,方便观察运行流程
                    ch.pipeline().addLast("encode", new EncoderHandler());//编码器。发送消息时候用过
                    ch.pipeline().addLast("decode", new DecoderHandler());//解码器,接收消息时候用
                    ch.pipeline().addLast("handler", new TcpServerHandler());//业务处理类,最终的消息会在这个handler中进行业务处理
                }
            });
            //使用了Future来启动线程,并绑定了端口
            ChannelFuture future = bootstrap.bind(port).sync();
            //以异步的方式关闭端口
            future.channel().closeFuture().sync();
        } catch (Exception e) {
        } finally {
            work.shutdownGracefully();
            //出现异常后,关闭线程组
            boss.shutdownGracefully();
        }

    }
}

2. 服务端解码器(decode)

public class DecoderHandler extends ByteToMessageDecoder {

    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("byteBuf的容量为:" + in.capacity());
        System.out.println("byteBuf的可读容量为:" + in.readableBytes());
        System.out.println("byteBuf的可写容量为:" + in.writableBytes());
        byte[] data = new byte[in.readableBytes()];
        //读取核心的数据
        in.readBytes(data);
        out.add(data);
    }
}

3. 服务端编码器(encode)

public class EncoderHandler extends MessageToByteEncoder {
    protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        out.writeBytes((byte[]) msg);
    }
}

4. 服务端的ChannelHandler

public class TcpServerHandler extends ChannelInboundHandlerAdapter {


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        byte[] recevierByte = (byte[]) msg;
        String recevierString = ByteTransform.bytesToHexString(recevierByte);
        System.out.println("-------------------长度为:" + recevierString.length());
        System.out.println("---tcp服务接受设备端加密数据:" + recevierString);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
    }

    /**
     * 客户端断开连接触发方法
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
    }

    /**
     * 方法中报错,触发方法
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
    }
}

5. 服务端启动类

public class StartServer {
    public static void main(String[] args) {
        try {
            new TcpServer(5566).init();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6. 工具类(知道方法是干嘛的就行)

public class ByteTransformIntUtil {
    /**
     * 根据byte转化为int
     */
    public static int getIntFromBytes(byte high_h, byte high_l, byte low_h, byte low_l) {
        return (high_h & 0xff) << 24 | (high_l & 0xff) << 16 | (low_h & 0xff) << 8 | low_l & 0xff;
    }
    /**
     * 根据byte转化为int
     */
    public static int getIntFromBytes(byte low_h, byte low_l) {
        return ByteTransformIntUtil.getIntFromBytes((byte)0,(byte)0,low_h,low_l);
    }
}

7. 客户端的配置类

public class MyRPCClient {

    public void start(String host, int port) throws Exception {
        //定义工作线程组
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            //注意:client使用的是Bootstrap
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(worker)
                    .channel(NioSocketChannel.class) //注意:client使用的是NioSocketChannel
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new MyClientHandler());
                        }
                    });
            //连接到远程服务
            ChannelFuture future = bootstrap.connect(host, port).sync();
            future.channel().closeFuture().sync();

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

8. 客户端启动类

public class StartClient {
    public static void main(String[] args) {
        new Thread() {
            public void run() {
                try {
                    new MyRPCClient().start("127.0.0.1", 5566);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

9. 客户端ChannelHandler

public class MyClientHandler extends SimpleChannelInboundHandler<Object> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object buf) throws Exception {
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 向服务端发送数据
        Timer timer = new Timer();
        final byte[] bytes = ByteTransform.hexStringToBytes("10000000");
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                byte[] msg1 = {0x00,0x00,0x00,0x00,0x01, 0x01, 0x02,0x01};
                ctx.writeAndFlush(Unpooled.copiedBuffer(msg1));
            }
        }, 5000, 50);
    }


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

    public int getIntFromBytes(byte high_h, byte high_l, byte low_h, byte low_l) {
        return (high_h & 0xff) << 24 | (high_l & 0xff) << 16 | (low_h & 0xff) << 8 | low_l & 0xff;
    }
}

1.1.2 对结果进行分析

image

查看客户端的Handler,向服务端发送数据的方法,是以50毫秒为间隔发送数据。但是服务端没有出现粘包问题。
总结 客户端向服务端有间隔时间向服务端发送数据,并不会出现粘包问题。

1.2 出现粘包问题

1.2.1 代码实例

客户端不间断的向服务端发字节数组(16个字节)

  1. 客户端ChannelHandler(只需要将上面客户端的MyClientHandler进行替换,其他代码依旧)
public class MyClientHandler extends SimpleChannelInboundHandler<Object> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object buf) throws Exception {

    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
       
        while (true){
            i = i + 1;
            byte[] msg = {0x00, 0x00, 0x00, 0x0c, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109, 98};
            ctx.writeAndFlush(Unpooled.copiedBuffer(msg));
        }
    }


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

    public int getIntFromBytes(byte high_h, byte high_l, byte low_h, byte low_l) {
        return (high_h & 0xff) << 24 | (high_l & 0xff) << 16 | (low_h & 0xff) << 8 | low_l & 0xff;
    }
}

1.2.2 对结果进行分析

客户端不间断向服务端发送字节数组(16个字节),服务将接收的字节打印出来。

image

现象: 每次读取的字节长度发生了改变。
总结: 如果客户端同事接收多比数据会出现粘包问题。

二 拆包

2.1 不出现拆包问题

2.1.1 代码实现

public class MyClientHandler extends SimpleChannelInboundHandler<Object> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object buf) throws Exception {
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 向服务端发送数据
        Timer timer = new Timer();
        final byte[] bytes = ByteTransform.hexStringToBytes("10000000");
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                byte[] msg1 = {0x00,0x00,0x00,0x00,0x01, 0x01, 0x02,0x01};
                ctx.writeAndFlush(Unpooled.copiedBuffer(msg1));
            }
        }, 5000, 50);
    }


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

    public int getIntFromBytes(byte high_h, byte high_l, byte low_h, byte low_l) {
        return (high_h & 0xff) << 24 | (high_l & 0xff) << 16 | (low_h & 0xff) << 8 | low_l & 0xff;
    }
}

2.1.2 结果分析

image

查看客户端的Handler,向服务端发送数据的方法,是以50毫秒为间隔发送数据。但是服务端没有出现粘包问题。
总结客户端向服务端发送若字节数组(十几个字节),并不会出现拆包问题。

2.2 实例netty拆包问题(一)

public class MyClientHandler extends SimpleChannelInboundHandler<Object> {

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object buf) throws Exception {
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 向服务端发送数据
        Timer timer = new Timer();
        final byte[] bytes = ByteTransform.hexStringToBytes("10000000");
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                byte[] msg = {0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                            0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,


                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                        0x01, 98, 49, 50, 51, 52, 53, 54, 0x12, 0x11, 0x11, 0x00, 0x00, 109, 109,
                };
                ctx.writeAndFlush(Unpooled.copiedBuffer(msg));
            }
        }, 5000, 1000);
    }


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

    public int getIntFromBytes(byte high_h, byte high_l, byte low_h, byte low_l) {
        return (high_h & 0xff) << 24 | (high_l & 0xff) << 16 | (low_h & 0xff) << 8 | low_l & 0xff;
    }

}

image

现象 服务端接收前两次之数据之后,服务端接收客户端发过来的数据都是定长的。前两次数据长度之后为后面数据之和。
分析 ByteBuf初始容量是一个300-1024随机值,当接收到的数据长度大于ByteBuf的容量的时候,ByteBuf分两次来接收.造成拆包效果.

三 解决粘包和拆包问题

2.1 方案一

2.1.1 代码实现

思路 在传递数据前加上固定包头,存储数据的长度.每次读取规定的长度.

  1. 服务端decode(解码器)
public class DecoderHandler extends ByteToMessageDecoder {

    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

        System.out.println("byteBuf的容量为:" + in.capacity());
        System.out.println("byteBuf的可读容量为:" + in.readableBytes());
        System.out.println("byteBuf的可写容量为:" + in.writableBytes());
        int len = ByteTransformIntUtil.getIntFromBytes(in.readByte(), in.readByte());
        byte[] data = new byte[len];
        //读取核心的数据
        in.readBytes(data);
        out.add(data);
    }
}
  1. 客户端的Handler

2.1.2 对结果分析

image

现象 服务端发生报错.
分析 因为客户端传过来规定数据长度为1000个字节以上,从ByteBuf取出的字节长度大于ByteBuf容量.
代码分析

//规定客户端传递的数据长度
int len = ByteTransformIntUtil.getIntFromBytes(in.readByte(), in.readByte());
//规定字节数组长度
byte[] data = new byte[len];
//读取核心的数据
in.readBytes(data);

注意 chennelPipeline 里面是一个个得ChannelHandler,入站得时候从第一个ChannelHandler执行,一次执行到尾部.出战是从尾执行到头.但是出站的时候,不执行解码器.进站时候,不执行编码器.我是用代码debug调试的,这里就演示了.感兴趣可以自己写个demo试试.

2.2 方案二(解决了)

2.2.1 代码实现

思路 方案一,是因为传递数据长度大于ByteBuf长度,读取报错.那就如果接收数据长度大于BetuBuf长度,让这次读取返回.和下次一起读取.(这样解决数据读取不完整,也解决数据粘包)

    1. 服务端decode(解码器)
public class DecoderHandler extends ByteToMessageDecoder {

    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("byteBuf的容量为:" + in.capacity());
        System.out.println("byteBuf的可读容量为:" + in.readableBytes());
        System.out.println("byteBuf的可写容量为:" + in.writableBytes());
        //将当前ByteBuf的读指针,存在一个变量m中
        in.markReaderIndex();
        int len = ByteTransformIntUtil.getIntFromBytes(in.readByte(), in.readByte());
        System.out.println("数据实际长度:" + len);
        if(len > in.readableBytes()){
            //将读指针恢复到刚才标记的变量值
            in.resetReaderIndex();
            //如果接收数据长度大于ByteBuf长度时候,将不进行此次读取和下次一起读取.
            return;
        }
        byte[] data = new byte[len];
        //读取核心的数据
        in.readBytes(data);
        out.add(data);
    }
}

2.2.2 对结果分析

客户端不间断的向服务端发送字节数据(长度为1710),服务端将接收数据打印出来

image

现象 服务端打印字节数组长度全为1710个字节.
总结 解决了TCP使用Netty的粘包和拆包问题.

2.2.3 源码(最终解决方案)

GitHub代码地址(点击获取)
gitee代码地址

掘金年度征文 | 2020 与我的技术之路 征文活动正在进行中......

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容