netty源码阅读之解码之基于长度域解码器分析

基于长度域解码器LengthFieldBasedFrameDecoder我们主要分析以下三点:

1、计算需要抽取的数据包的长度

2、跳过字节逻辑处理

3、丢弃模式下的处理

首先源码还是LengthFieldBasedFrameDecoder的decode方法:

    /**
     * Create a frame out of the {@link ByteBuf} and return it.
     *
     * @param   ctx             the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
     * @param   in              the {@link ByteBuf} from which to read data
     * @return  frame           the {@link ByteBuf} which represent the frame or {@code null} if no frame could
     *                          be created.
     */
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        if (discardingTooLongFrame) {
            long bytesToDiscard = this.bytesToDiscard;
            int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
            in.skipBytes(localBytesToDiscard);
            bytesToDiscard -= localBytesToDiscard;
            this.bytesToDiscard = bytesToDiscard;

            failIfNecessary(false);
        }

        if (in.readableBytes() < lengthFieldEndOffset) {
            return null;
        }

        int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
        long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);

        if (frameLength < 0) {
            in.skipBytes(lengthFieldEndOffset);
            throw new CorruptedFrameException(
                    "negative pre-adjustment length field: " + frameLength);
        }

        frameLength += lengthAdjustment + lengthFieldEndOffset;

        if (frameLength < lengthFieldEndOffset) {
            in.skipBytes(lengthFieldEndOffset);
            throw new CorruptedFrameException(
                    "Adjusted frame length (" + frameLength + ") is less " +
                    "than lengthFieldEndOffset: " + lengthFieldEndOffset);
        }

        if (frameLength > maxFrameLength) {
            long discard = frameLength - in.readableBytes();
            tooLongFrameLength = frameLength;

            if (discard < 0) {
                // buffer contains more bytes then the frameLength so we can discard all now
                in.skipBytes((int) frameLength);
            } else {
                // Enter the discard mode and discard everything received so far.
                discardingTooLongFrame = true;
                bytesToDiscard = discard;
                in.skipBytes(in.readableBytes());
            }
            failIfNecessary(true);
            return null;
        }

        // never overflows because it's less than maxFrameLength
        int frameLengthInt = (int) frameLength;
        if (in.readableBytes() < frameLengthInt) {
            return null;
        }

        if (initialBytesToStrip > frameLengthInt) {
            in.skipBytes(frameLengthInt);
            throw new CorruptedFrameException(
                    "Adjusted frame length (" + frameLength + ") is less " +
                    "than initialBytesToStrip: " + initialBytesToStrip);
        }
        in.skipBytes(initialBytesToStrip);

        // extract frame
        int readerIndex = in.readerIndex();
        int actualFrameLength = frameLengthInt - initialBytesToStrip;
        ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength);
        in.readerIndex(readerIndex + actualFrameLength);
        return frame;
    }

一、计算需要抽取的数据包的长度

第一段代码是丢弃模式的,我们直接跳过看第二段:

        if (in.readableBytes() < lengthFieldEndOffset) {
            return null;
        }

lengthFiledEndOffset上一篇文章我们分析过,在LengthFieldBasedFrameDecoder的构造器里面初始化,

lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;

代表长度域偏移加上长度域长度。

那么in.readableBytes()<lengthFieldEndOffset的话,说明数据包不完整,连lengthFiledOffset的长度都达不到,数据包肯定不完整,所以返回空,然后累加器就会继续累加,知道数据包完整为止。

接下去看这一段:

 int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;

 lengthFiledOffset是我们的相对偏移量,加上readerIndex就是在这个byteBuf里面的绝对偏移量了,通过这个绝对偏移量,我们后续的操作就可以变得更加简单。

接下去看:

        long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);

通过in这个byteBuf,actualLengthFieldOffset实际偏移量和lengthFieldLength就能找出我们信息的长度。点击去看怎么实现的:

    /**
     * Decodes the specified region of the buffer into an unadjusted frame length.  The default implementation is
     * capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer.  Override this method to
     * decode the length field encoded differently.  Note that this method must not modify the state of the specified
     * buffer (e.g. {@code readerIndex}, {@code writerIndex}, and the content of the buffer.)
     *
     * @throws DecoderException if failed to decode the specified region
     */
    protected long getUnadjustedFrameLength(ByteBuf buf, int offset, int length, ByteOrder order) {
        buf = buf.order(order);
        long frameLength;
        switch (length) {
        case 1:
            frameLength = buf.getUnsignedByte(offset);
            break;
        case 2:
            frameLength = buf.getUnsignedShort(offset);
            break;
        case 3:
            frameLength = buf.getUnsignedMedium(offset);
            break;
        case 4:
            frameLength = buf.getUnsignedInt(offset);
            break;
        case 8:
            frameLength = buf.getLong(offset);
            break;
        default:
            throw new DecoderException(
                    "unsupported lengthFieldLength: " + lengthFieldLength + " (expected: 1, 2, 3, 4, or 8)");
        }
        return frameLength;
    }

简单,就是在buf里面找到长度域的位置,然后取出长度域里面的值,作为frameLength返回。

然后:

        if (frameLength < 0) {
            in.skipBytes(lengthFieldEndOffset);
            throw new CorruptedFrameException(
                    "negative pre-adjustment length field: " + frameLength);
        }

如果取出的frameLength<0,那就是不符合条件的,抛出异常。

然后:

frameLength += lengthAdjustment + lengthFieldEndOffset;

我们上一篇讲过一个调整值,长度域里面内容不一定表示的就是实际内容的长度,它可能加上长度域的长度或者head或者两者都有,所以我们通过这个调整值来调整。

所以最终frameLength就是frameLength加上调整值,再加上lengthEndOffset。

我们以以下这个图作为例子:

由于长度域表示的值是8,这个8包括了长度域2和长度域前面的两个内容2,当然还有最终的信息4,所以是8,所以需要调整 -4,也就是减去长度域2和长度域之前的内容2。最终上面那个表达式的值就是8,也就是最终读取到的整段信息的内容。

然后:

        if (frameLength < lengthFieldEndOffset) {
            in.skipBytes(lengthFieldEndOffset);
            throw new CorruptedFrameException(
                    "Adjusted frame length (" + frameLength + ") is less " +
                    "than lengthFieldEndOffset: " + lengthFieldEndOffset);
        }

这个frameLength是经过调整之后的frameLength,经过调整之后的frameLength是整段信息内容也就是包括lengthFieldEndOffset的,但是现在小于lengthFieldEndOffset明显不对,跳过lengthFieldEndOffset这段内容,并且抛出异常。

接下去一段的内容是丢弃模式的处理,我们放最后。

二、跳过字节逻辑处理

看这一段:

        // never overflows because it's less than maxFrameLength
        int frameLengthInt = (int) frameLength;
        if (in.readableBytes() < frameLengthInt) {
            return null;
        }

到了这里就说明我们的数据是正确的,没有超过maxFrameLength。先转换为10进制。

如果当前可读的内容小于我们需要读取的内容,那么就说明数据包不完整,继续回去读。

接下去:

        if (initialBytesToStrip > frameLengthInt) {
            in.skipBytes(frameLengthInt);
            throw new CorruptedFrameException(
                    "Adjusted frame length (" + frameLength + ") is less " +
                    "than initialBytesToStrip: " + initialBytesToStrip);
        }

如果需要跳过的直接大于当前已有的字节,当然也是不对的,继续抛出异常。

最后才是真正跳过字节逻辑的处理:

in.skipBytes(initialBytesToStrip);

取出readerIndex,并且求出真正的跳过之后信息的长度:

        // extract frame
        int readerIndex = in.readerIndex();
        int actualFrameLength = frameLengthInt - initialBytesToStrip;

最后把需要的信息提取出来,并且移动指针,然后把取到的信息返回:

        ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength);
        in.readerIndex(readerIndex + actualFrameLength);
        return frame;

三、丢弃模式下的处理

我们先分析代码是如何进入丢弃模式的,也就是这一段:

        if (frameLength > maxFrameLength) {
            long discard = frameLength - in.readableBytes();
            tooLongFrameLength = frameLength;

            if (discard < 0) {
                // buffer contains more bytes then the frameLength so we can discard all now
                in.skipBytes((int) frameLength);
            } else {
                // Enter the discard mode and discard everything received so far.
                discardingTooLongFrame = true;
                bytesToDiscard = discard;
                in.skipBytes(in.readableBytes());
            }
            failIfNecessary(true);
            return null;
        }

当解析到的frameLength大于maxFrameLength了,那就是可能要进入丢弃模式了。

discard=frameLength-in.readableBytes

frameLength本来这一段都是要丢弃的,但是有可能当前可读的数据还小于frameLength的长度,那就还有discard这么多的字节,后续需要丢弃。

如果discard<0,说明当前可读的大于frameLength长度了,frameLength这么长的数据就直接丢弃:

 if (discard < 0) {
                // buffer contains more bytes then the frameLength so we can discard all now
                in.skipBytes((int) frameLength);
            }

否则,就要进入丢弃模式:

进入丢弃模式,并且需要记录剩下的还没有丢弃的数据,然后跳过当前可读的数据。

          if (discard < 0) {
               ...
            } else {
                // Enter the discard mode and discard everything received so far.
                discardingTooLongFrame = true;
                bytesToDiscard = discard;
                in.skipBytes(in.readableBytes());
            }

最后,调用failIfNecessary,因为是第一次,所以我们firstDetectionOfTooLongFrame为true。

我们看failIfNecessary:


    private void failIfNecessary(boolean firstDetectionOfTooLongFrame) {
        if (bytesToDiscard == 0) {
            // Reset to the initial state and tell the handlers that
            // the frame was too large.
            long tooLongFrameLength = this.tooLongFrameLength;
            this.tooLongFrameLength = 0;
            discardingTooLongFrame = false;
            if (!failFast ||
                failFast && firstDetectionOfTooLongFrame) {
                fail(tooLongFrameLength);
            }
        } else {
            // Keep discarding and notify handlers if necessary.
            if (failFast && firstDetectionOfTooLongFrame) {
                fail(tooLongFrameLength);
            }
        }
    }

如果还需要丢弃的数据为0,那就可以结束丢弃模式了,并且tooLongFrameLength的值赋给别的变量,方便做后续异常处理的参数。如果是非快速失败模式,或者是快速失败模式,并且是第一次进来(因为我们上一步判断discard的时候只用了>0,没有考虑到=0的情况,所以也有可能进入到这个判断,这种情况是第一次进入丢弃模式,但是马上在这里又还原了,所以也可以调用fail方法),那就调用fail抛出异常。

否则,符合failFast模式并且是第一次进来也是抛出异常。

然后我们看函数一开始进来的代码:

        if (discardingTooLongFrame) {
            long bytesToDiscard = this.bytesToDiscard;
            int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
            in.skipBytes(localBytesToDiscard);
            bytesToDiscard -= localBytesToDiscard;
            this.bytesToDiscard = bytesToDiscard;

            failIfNecessary(false);
        }

这里就是在丢弃模式下需要处理的事情了。

比较需要丢弃的数据的长度和可读的长度,取最小值,就是是我们当前可以丢弃的长度:

            int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());

然后跳过当前可以跳过的长度:

            in.skipBytes(localBytesToDiscard);

最后把之前需要丢弃的长度,减去当前丢弃了的长度,就得到还需要丢弃的长度,这个还需要丢弃的长度>=0,大于0说明可读长度还小于bytesToDiscard,还有内容需要丢弃,等于0说明都丢弃完了;然后记录还需要丢弃的长度。

            bytesToDiscard -= localBytesToDiscard;
            this.bytesToDiscard = bytesToDiscard;

最后还是调用failIfNecessary,但是这次不再是第一次了,所以firstDetectionOfTooLongFrame传入false。和之前第一次的failIfNecessary比较一下,会有更加深入的理解。

猜你喜欢

转载自blog.csdn.net/fst438060684/article/details/82915206
今日推荐