解决Netty消息超长导致拆成多条消息(拆包)

遇到问题的场景

问题的起源是因为帮朋友用socket做一个消息中转,然后就发现了他每次发送的消息都特别长导致channelread会读到多次,最开始图了省事手动去处理的拆包(这种比较恶心,要协定好消息的前缀和后缀才能去灵活的拼接消息,代码写起来也比较恶心,但是好在可以实现),然后研究了一下正常的解决方案,自定义协议

解决问题

首先双方需要协商一下协议所需要包含的内容,比如

  1. 头部信息,可以包含一些特殊信息可以被解析出来并特殊处理
  2. 消息长度,设定固定的长度
  3. 消息体内容
  4. 等等其他需要包含的信息

开始定义一个可以解决我如上所述问题的协议,然后定义一个对象包含如下信息

private int head = 824; //这里自定义
    private int contentLength; //content的长度
    private byte[] content; //消息的具体内容

自定义解码器(客户端是普通socket不是netty所以只自定义了解码)

public class YuDecode extends ByteToMessageDecoder {
    private final int LENGTH = 4 + 4 ;
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        //head 4字节
        //length 4字节
        if(in.readableBytes() >= LENGTH){
            //符合协议
            if(in.readableBytes() > 16392){
                System.out.println("超过协议定义大小" + in.readableBytes());
                //限制数据大小
                in.skipBytes(in.readableBytes());
            }
            int begin;
            while(true){
                //开始的位置
                begin = in.readerIndex();
                //标记
                in.markReaderIndex();
                if(in.readInt() == 824){
                    //每次读4个字节 读到了包头 符合要求
                    System.out.println("读到了包头");
                    break;
                }
                //没读到包头重置读取位置
                in.resetReaderIndex();
                //略过一个字节重新读
                in.readByte();
                if(in.readableBytes() < LENGTH){
                    System.out.println("没有读到包头");
                    return;
                }
            }
            //消息长度
            int contentLength = in.readInt();
            if(in.readableBytes() < contentLength){
                System.out.println("消息的内容长度没有达到预期设定的长度,还原指针重新读");
                //消息的内容长度没有达到预期设定的长度,还原指针重新读
                in.readerIndex(begin);
                return;
            }
            byte[] content = new byte[contentLength];
            in.readBytes(content);
            YuSocket protocol = new YuSocket();
            protocol.setHead(824);
            protocol.setContent(content);
            protocol.setContentLength(contentLength);
            System.out.println(contentLength);
            out.add(protocol);
        }
        System.out.println("不符合协议内容");
    }
}

配置初始化通道和nettyserver

NioEventLoopGroup workGroup = new NioEventLoopGroup();
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(workGroup,bossGroup);
        serverBootstrap.channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new LoggingHandler("DEBUG"));
                        ch.pipeline().addLast(new YuEncode());
                        ch.pipeline().addLast(new YuDecode());
                        ch.pipeline().addLast(new ServerHandler());
                    }
                });
        try {
            ChannelFuture sync = serverBootstrap.bind(8080).sync();
            sync.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

测试的socket客户端发送的长度为16384字节,比较大了吧

Socket socket = new Socket("127.0.0.1",8080);
            socket.setKeepAlive(true);
            boolean connected = socket.isConnected();
            OutputStream outputStream = socket.getOutputStream();
            String ss="太长了...";
            YuSocket yuSocket = new YuSocket();
            yuSocket.setHead(824);
            yuSocket.setContentLength(ss.length());
            yuSocket.setContent(ss.getBytes());
            byte[] bytes = toByteArray(yuSocket);
            System.out.println(bytes.length);
            outputStream.write(bytes);
            outputStream.flush();

这样完整的一条信息就读出来了,不用分批次接受手动组装了.

android Netty连接成功后就断开了_自定义