2019.9.10笔记——netty解码器源码分析

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_35262405/article/details/100711748

DelimiterBasedFrameDecoder

这是一个基于分隔符的解码器,并且可以自定义分隔符

第一个参数是能够接收数据的最大长度,后面的可变参数是分隔符,可以传递多个。

DelimiterBasedFrameDecoder delimiterBasedFrameDecoder =
        new DelimiterBasedFrameDecoder(10, Unpooled.wrappedBuffer(new byte[]{'\n'}),Unpooled.wrappedBuffer(new byte[]{'\r','\n'}));

需要注意的是如果分隔符是\n\r\n那么会直接使用LineBasedFrameDecoder解码器来解码,不过传入的分隔符对象是很严格定义的

构造器方法,会判断传递过来的分隔符是不是\n\r\n,如果是的话会创建一个LineBasedFrameDecoder解码器,并赋值给lineBasedDecoder,会面在解码的时候会判断时候已经创建了解码器,如果创建了的话会直接用此解码器

public DelimiterBasedFrameDecoder(
         int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters) {
     validateMaxFrameLength(maxFrameLength);
     if (delimiters == null) {
         throw new NullPointerException("delimiters");
     }
     if (delimiters.length == 0) {
         throw new IllegalArgumentException("empty delimiters");
     }

     if (isLineBased(delimiters) && !isSubclass()) {
         lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
         this.delimiters = null;
     } else {
         this.delimiters = new ByteBuf[delimiters.length];
         for (int i = 0; i < delimiters.length; i ++) {
             ByteBuf d = delimiters[i];
             validateDelimiter(d);
             this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes());
         }
         lineBasedDecoder = null;
     }
     this.maxFrameLength = maxFrameLength;
     this.stripDelimiter = stripDelimiter;
     this.failFast = failFast;
 }

解码方法

@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);
    }
}

真正的解码方法,在调用过寻找分隔符的索引的方法后会出现4种情况,分情况会做不同的处理。

  1. 没有找到分隔符,超过最大长度,直接跳过本次要读取的数据(跳过到分隔符之前的数据),抛出异常
  2. 没有找到分隔符,超过最大长度,直接跳过整个buffer中的数据,设置开启丢弃模式,根据failFast属性判断是否马上抛出异常
  3. 找到了分隔符,处于丢弃模式,设置关闭丢弃模式,跳过本次要读取的数据(跳过到分隔符之前的数据),设置已经丢弃的字节长度为0,根据failFast属性判断是否马上抛出异常
  4. 没有找到分隔符,没有处于丢弃模式,累加丢弃的直接,丢弃整个buffer中的数据
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
    //首先判断行解码器是否被实例化了   被实例化了就使用行处理器
    if (lineBasedDecoder != null) {
        return lineBasedDecoder.decode(ctx, buffer);
    }
    // 尝试所有分隔符并选择产生最短帧的分隔符。
    int minFrameLength = Integer.MAX_VALUE;
    ByteBuf minDelim = null;
    for (ByteBuf delim: delimiters) {
        //找到最小分隔符的位置
        int frameLength = indexOf(buffer, delim);
        if (frameLength >= 0 && frameLength < minFrameLength) {
            //本次有效数据长度
            minFrameLength = frameLength;
            minDelim = delim;
        }
    }
    //如果找到了
    if (minDelim != null) {
        //分隔符长度
        int minDelimLength = minDelim.capacity();
        ByteBuf frame;

        //如果处于丢弃模式
        if (discardingTooLongFrame) {
            // We've just finished discarding a very large frame.
            // Go back to the initial state.
            //设置为非丢弃模式
            discardingTooLongFrame = false;
            //跳过本次要截取的数据
            buffer.skipBytes(minFrameLength + minDelimLength);

            //设置丢弃的长度为0
            int tooLongFrameLength = this.tooLongFrameLength;
            this.tooLongFrameLength = 0;
            if (!failFast) {
                fail(tooLongFrameLength);
            }
            return null;
        }
        //本次要截取的数据大于最大能截取的长度
        if (minFrameLength > maxFrameLength) {
            //丢弃本次可读数据加分割符
            buffer.skipBytes(minFrameLength + minDelimLength);
            fail(minFrameLength);
            return null;
        }

        //有效数据是否要截取分隔符
        if (stripDelimiter) {
            frame = buffer.readRetainedSlice(minFrameLength);
            buffer.skipBytes(minDelimLength);
        } else {
            frame = buffer.readRetainedSlice(minFrameLength + minDelimLength);
        }

        return frame;
    } else {
        //如果没有找到分割符
        //判断是否处于非丢弃模式
        if (!discardingTooLongFrame) {
            //如果本次可读取的数据长度大于最大可读取长度
            if (buffer.readableBytes() > maxFrameLength) {
                // 丢弃缓冲区的内容,直到找到分隔符为止。
                //丢弃的长度等于本次可读的长度
                tooLongFrameLength = buffer.readableBytes();
                //丢弃
                buffer.skipBytes(buffer.readableBytes());
                //设置丢弃模式为true
                discardingTooLongFrame = true;
                if (failFast) {
                    fail(tooLongFrameLength);
                }
            }
        } else {
            //仍然丢弃缓冲区,因为没有找到分隔符。
            tooLongFrameLength += buffer.readableBytes();
            buffer.skipBytes(buffer.readableBytes());
        }
        return null;
    }
}

找到分隔符的索引的方法

private static int indexOf(ByteBuf haystack, ByteBuf needle) {
    //外层遍历需要读取的数据ByteBuf
    for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) {
        int haystackIndex = i;
        int needleIndex;
        //内层循环遍历分隔符的ByteBuf
        for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) {
            if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) {
                break;
            } else {
                haystackIndex ++;
                //这个if判断需要读取的数据是否已经达到了写指针的位置
                //如果到了而且分隔符还没有遍历完的话就会直接返回-1
                //防止读取数据的ByteBuf的索引超过写指针
                if (haystackIndex == haystack.writerIndex() &&
                    needleIndex != needle.capacity() - 1) {
                    return -1;
                }
            }
        }

        //代码执行到这说明找到了分隔符
        //这里是判断分隔符是否已经遍历完了
        //如果遍历完就会把需要读取的数据的索引返回出去,这里把分隔符的索引减去了
        if (needleIndex == needle.capacity()) {
            // Found the needle from the haystack!
            return i - haystack.readerIndex();
        }
    }
    return -1;
}

LengthFieldBasedFrameDecoder

这是一个基于长度的解码器,这种解码器会把数据头的数值当作需要读取的数据的长度。

所以会规定数据头存储数据长度的数值的长度,同时在传输数据时会见数据的长度放在数据的头。

LengthFieldBasedFrameDecoder的一些参数

public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {

    private final ByteOrder byteOrder;
    //接收数据的最大长度
    private final int maxFrameLength;
    //长度字段的偏差
    private final int lengthFieldOffset;
    //长度字段的字节数
    private final int lengthFieldLength;
    //长度字段的偏差+长度字段占的字节数
    private final int lengthFieldEndOffset;
    //添加到长度字段的补偿值
    private final int lengthAdjustment;
    //需要跳过的字节数(跳过长度字段)
    private final int initialBytesToStrip;
    private final boolean failFast;
    private boolean discardingTooLongFrame;
    private long tooLongFrameLength;
    //还需要丢弃多少就字节
    private long bytesToDiscard;

猜你喜欢

转载自blog.csdn.net/qq_35262405/article/details/100711748