美文网首页
当客户端发送过大数据时,netty分包解决方案

当客户端发送过大数据时,netty分包解决方案

作者: 凉风拂面秋挽月 | 来源:发表于2020-03-28 22:46 被阅读0次

    遇到问题:当在一个项目中通过socket向netty服务器一次性发送近40kb的数据时,在netty服务端发生分包。

    服务端代码:

    public class HeartBeatServer {
        private static int port = 5200;
    
        @SuppressWarnings("static-access")
        public HeartBeatServer(int port) {
            this.port = port;
        }
    
        ServerBootstrap bootstrap = null;
        ChannelFuture f;
       
    
        public static void main(String args[]) {
            HeartBeatServer heartBeatServer = new HeartBeatServer(port);
            heartBeatServer.startServer();
        }
     
        public void startServer() {
            EventLoopGroup bossgroup = new NioEventLoopGroup();
            EventLoopGroup workergroup = new NioEventLoopGroup();
            try {
                bootstrap = new ServerBootstrap();
                bootstrap.group(bossgroup, workergroup)
                        .channel(NioServerSocketChannel.class)
                        .childHandler(new HeartBeatServerInitializer());                   
                f = bootstrap.bind(port).sync();
                System.out.println("server start ,port: "+port);
                f.channel().closeFuture().sync();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                bossgroup.shutdownGracefully();
                workergroup.shutdownGracefully();
            }
        }
    
    
        private class HeartBeatServerInitializer extends ChannelInitializer<SocketChannel> {
    
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline pipeline = ch.pipeline();             
                pipeline.addLast("decoder", new StringDecoder());
                pipeline.addLast("encoder", new StringEncoder());
                pipeline.addLast("handler", new HeartbeatServerHandler());
            }
        }
    }
    

    解决方案如下:

      ByteBuf delimiter = Unpooled.copiedBuffer("\t".getBytes());
                pipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024,delimiter));
    

    DelimiterBasedFrameDecoder的构造方法

    public DelimiterBasedFrameDecoder(int maxFrameLength, boolean stripDelimiter, ByteBuf delimiter) {
            this(maxFrameLength, stripDelimiter, true, delimiter);
    }
    

    maxFrameLength:解码的帧的最大长度
    stripDelimiter:解码时是否去掉分隔符
    failFast:为true,当frame长度超过maxFrameLength时立即报TooLongFrameException异常,为false,读取完整个帧再报异常
    delimiter:分隔符
    同样,客户端需要在发送消息的尾部加入一个"\t",不用担心服务端,服务端在接受时会自动忽略到这个\t。

    分包解决方案

    1.消息长度固定,累计读取到消息长度总和为定长Len的报文之后即认为是读取到了一个完整的消息。计数器归位,重新读取。(FixedLengthFrameDecoder)
    2.将回车换行符作为消息结束符。(LineBasedframeDecoder)
    3.将特殊的分隔符作为消息分隔符,回车换行符是他的子集。(DelimiterBasedFrameDecoder)
    4.通过在消息头定义长度字段来标识消息总长度。

    四种解决方案中我选用了第三种
    LineBasedframeDecoder属于第二种,今天我们要说的DelimiterBasedFrameDecoder和FixedLengthFrameDecoder属于第三种和第一种。DelimiterBasedFrameDecoder用来解决以特殊符号作为消息结束符的粘包问题,FixedLengthFrameDecoder用来解决定长消息的粘包问题。

    第三种我们已经用了,那么来说一下第一种和第二种

    FixedLengthFrameDecoder

    FixedLengthFrameDecoder是固定长度解码器

    pipeline.addLast(new FixedLengthFrameDecoder(23));//参数为一次接受的数据长度
    

    这个的意思是只接收到23个字节,如果字节多余23个也不接受了直接给到channelread方法中,如果没有接够23个字节,则会一直阻塞。
    FixedLengthFrameDecoder构造方法如下

    public FixedLengthFrameDecoder(int frameLength, boolean allocateFullBuffer) {
            if (frameLength <= 0) {
                throw new IllegalArgumentException(
                        "frameLength must be a positive integer: " + frameLength);
            }
            this.frameLength = frameLength;
            this.allocateFullBuffer = allocateFullBuffer;
        }
    

    allocateFullBuffer默认为false

    LineBasedFrameDecoder

    按行分割协议
    LineBasedFrameDecoder 和LineEncoder采用的通信协议非常简单,即按照行进行分割,遇到一个换行符,则认为是一个完整的报文。在发送方,使用LineEncoder为数据添加换行符;在接受方,使用LineBasedFrameDecoder对换行符进行解码。
    LineBasedFrameDecoder采用的通信协议格式非常简单:使用换行符\n或者\r\n作为依据,遇到\n或者\r\n都认为是一条完整的消息。

    LineBasedFrameDecoder提供了2个构造方法,如下:

    public LineBasedFrameDecoder(final int maxLength) {
        this(maxLength, true, false);
    }
    public LineBasedFrameDecoder(final int maxLength, final boolean stripDelimiter, final boolean failFast) {
        this.maxLength = maxLength;
        this.failFast = failFast;
        this.stripDelimiter = stripDelimiter;
    }
    

    maxLength:
    表示一行最大的长度,如果超过这个长度依然没有检测到\n或者\r\n,将会抛出TooLongFrameException

    failFast:
    与maxLength联合使用,表示超过maxLength后,抛出TooLongFrameException的时机。如果为true,则超出maxLength后立即抛出TooLongFrameException,不继续进行解码;如果为false,则等到完整的消息被解码后,再抛出TooLongFrameException异常。

    stripDelimiter:
    解码后的消息是否去除\n,\r\n分隔符。

    使用例

    // 使用LineBasedFrameDecoder解决粘包问题,其会根据"\n"或"\r\n"对二进制数据进行拆分
     pipeline().addLast(new LineBasedFrameDecoder(1024, true, true));
    

    相关文章

      网友评论

          本文标题:当客户端发送过大数据时,netty分包解决方案

          本文链接:https://www.haomeiwen.com/subject/fljvgctx.html