netty粘包半包问题常见解决方案及总结

资料

netty处理器链初始化源码分析
netty处理器链读数据执行流程源码分析
LineBasedFrameDecoder解决粘包半包源码分析
netty粘包半包问题常见解决方案及总结
netty处理器链架构图及其总结

解决粘包半包主要目的

解决粘包半包问题其实就是围绕一个主题,那就是从TCP包中,通过一定字节作为标记,然后标记前字节必定是一个可反序列化的字节Buff, 这样就可以解决粘包半包问题

解决粘包方案

1.通过指定固定字节数作为分隔标准

2.指定特殊字符作为分隔标准

3.默认使用换行符作为分隔标准(指定特殊字符作为分隔标准一种情况)

解决粘包方案图解

在这里插入图片描述

FixedLengthFrameDecoder

//截取固定长度的字节作为一个包

//子类解码实现,由ByteToMessageDecoder主流程的channelRead调用
//子类实现,只是截取FixedLength应该截取的字节长度
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    
    
    Object decoded = decode(ctx, in);
    if (decoded != null) {
    
    
        out.add(decoded);
    }
}

//截取定义长度的frameLength的Buff
//1.如果长度不够,那么不截取,直接返回null,不执行下去(半包)
//2.如果长度大于等于frameLength长度,截取frameLength长度字节的Buff(粘包)
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
    
    
        if (in.readableBytes() < frameLength) {
    
    
            return null;
        } else {
    
    
            return in.readRetainedSlice(frameLength);
        }
    }

DelimiterBasedFrameDecoder

//根据指定的特殊字符分割作为一个包

//子类解码实现,由ByteToMessageDecoder主流程的channelRead调用
//子类实现,只是截取指定特殊字符前面的字节长度
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    
    
    Object decoded = decode(ctx, in);
    if (decoded != null) {
    
    
        out.add(decoded);
    }
}

protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    
    
      
        int minFrameLength = Integer.MAX_VALUE;
        ByteBuf minDelim = null;
        for (ByteBuf delim: delimiters) {
    
    
            //找到特殊字符的字节内容长度,根据delim去截取
            int frameLength = indexOf(buffer, delim);
            if (frameLength >= 0 && frameLength < minFrameLength) {
    
    
                //minFrameLength表示这个包的长度
                minFrameLength = frameLength;
                //
                minDelim = delim;
            }
        }
        
        //minFrameLength是包长度
        //minDelimLength是指定特殊字符的长度
        //每次一帧长度为minFrameLength+minDelimLength
        if (minDelim != null) {
    
    
            if (stripDelimiter) {
    
    
                frame = buffer.readRetainedSlice(minFrameLength);
                buffer.skipBytes(minDelimLength);
            } else {
    
    
                frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
            }

            return frame;
        } 
    }

LineBasedFrameDecoder

//根据指定的特殊字符分割作为一个包

//子类解码实现,由ByteToMessageDecoder主流程的channelRead调用
//子类实现,只是截取换行符前的字节长度

@Override
protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    
    
    Object decoded = decode(ctx, in);
    if (decoded != null) {
    
    
        out.add(decoded);
    }
}

//寻找到换行符然后截取子Buff
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    
    
        //找到换行符的位置eol
        final int eol = findEndOfLine(buffer);
        if (!discarding) {
    
    
            if (eol >= 0) {
    
    
                final ByteBuf frame;
                //eol是换行符前面字节长度
              	//delimLength是换行符长度
                //截取的总长度是eol+delimLength
                final int length = eol - buffer.readerIndex();
                final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;

                if (stripDelimiter) {
    
    
                    //获取完整包字节
                    frame = buffer.readRetainedSlice(length);
                    //跳过换行符长度字节数
                    buffer.skipBytes(delimLength);
                } else {
    
    
                    frame = buffer.readRetainedSlice(length + delimLength);
                }

                return frame;
            } 
    }

猜你喜欢

转载自blog.csdn.net/weixin_38312719/article/details/108806499