netty源码阅读之解码之基于分隔符解码器分析

基于分隔符解码器DelimiterBasedFrameDecoder也算是比较简单的,和基于行解码器差不多一样分析既可,首先我们看一个构造函数:

    public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf... delimiters) {
        this(maxFrameLength, true, delimiters);
    }

这个构造函数有个maxFrameLength,这个也就是我们能解析的字符最大多大,大于这个的解析出来的内容都要丢弃。

然后这个ByteBuf... delimiters的参数,表示可以有多个分隔符。

我们还是来到它的decode方法:

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

一样的把解析到的数据放到decode里面,然后我们看最重要的自己的decode方法:

    /**
     * Create a frame out of the {@link ByteBuf} and return it.
     *
     * @param   ctx             the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to
     * @param   buffer          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 buffer) throws Exception {
        if (lineBasedDecoder != null) {
            return lineBasedDecoder.decode(ctx, buffer);
        }
        // Try all delimiters and choose the delimiter which yields the shortest frame.
        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);

                int tooLongFrameLength = this.tooLongFrameLength;
                this.tooLongFrameLength = 0;
                if (!failFast) {
                    fail(tooLongFrameLength);
                }
                return null;
            }

            if (minFrameLength > maxFrameLength) {
                // Discard read frame.
                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) {
                    // Discard the content of the buffer until a delimiter is found.
                    tooLongFrameLength = buffer.readableBytes();
                    buffer.skipBytes(buffer.readableBytes());
                    discardingTooLongFrame = true;
                    if (failFast) {
                        fail(tooLongFrameLength);
                    }
                }
            } else {
                // Still discarding the buffer since a delimiter is not found.
                tooLongFrameLength += buffer.readableBytes();
                buffer.skipBytes(buffer.readableBytes());
            }
            return null;
        }
    }

其实也不难,分三步分析它:

1、行处理器

2、找到最小分隔符

3、解码

一、行处理器

看这个源码:
 

        if (lineBasedDecoder != null) {
            return lineBasedDecoder.decode(ctx, buffer);
        }

如果行处理器不为空,则调用行处理器。我们可以很简单地知道,他之前判断过分隔符是不是行分隔符,如果是,就给lineBaaseDecoder赋值,到了这里,就会进入这个if。lineBasedDecoder在什么地方初始化呢?

在DelimiterBasedFrameDecoder的构造函数里面有这样一个逻辑:

if (isLineBased(delimiters) && !isSubclass()) {
            lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
            this.delimiters = null;
        } else {

 如果判断是行分隔符那就实例化行处理器,给lineBasedDecoder赋值。看isLineBase方法的源码:

    /** Returns true if the delimiters are "\n" and "\r\n".  */
    private static boolean isLineBased(final ByteBuf[] delimiters) {
        if (delimiters.length != 2) {
            return false;
        }
        ByteBuf a = delimiters[0];
        ByteBuf b = delimiters[1];
        if (a.capacity() < b.capacity()) {
            a = delimiters[1];
            b = delimiters[0];
        }
        return a.capacity() == 2 && b.capacity() == 1
                && a.getByte(0) == '\r' && a.getByte(1) == '\n'
                && b.getByte(0) == '\n';
    }

很简单地逻辑,delimiters长度必须为2并且里面的内容必须为\n、\r\n,就可以判断为行处理器了。 

二、找到最小分隔符

这个也是比较简单的几行代码

        // Try all delimiters and choose the delimiter which yields the shortest frame.
        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;
            }
        }

通过indexOf找到当前分隔符分割的长度,和最小的比较,如果小于最小就交换,并且把当前的分隔符作为最小分隔符。 

三、解码

这个看起来复杂,其实很简单。和上篇文章一样分析,源码自行查看,很简单。

1、如果找到了分隔符(这个分隔符是最小分隔符)

    A、如果是丢弃模式

    设置为非丢弃模式;丢弃掉之前记录的数据;如果不是快速失败,就在这里抛出异常。这个抛出异常的fail也很常见:

    private void fail(long frameLength) {
        if (frameLength > 0) {
            throw new TooLongFrameException(
                            "frame length exceeds " + maxFrameLength +
                            ": " + frameLength + " - discarded");
        } else {
            throw new TooLongFrameException(
                            "frame length exceeds " + maxFrameLength +
                            " - discarding");
        }
    }

    B、如果不是丢弃模式

    如果解析到的数据大于maxFrameLength,丢弃掉这一段数据,并且用fail来抛出异常。

    如果解析到的数据小于等于maxFrameLength,说明这个数据正常,判断是否需要加上分割符之后返回。

2、如果没有找到分隔符

    A、如果不是丢弃模式

    如果readableBytes长度已经超过了最大可允许的长度,那就把要丢弃的长度记录下来,并且跳过这一段,并且标记进入丢弃模式,并且如果是快速丢弃,就从fail里面抛出异常;

   如果readableBytes长度没有超过了最大可允许的长度,不做任何操作,继续等以后的数据。

    B、如果是丢弃模式

    添加需要丢弃的字节到tooLongFrameLength里面,然后跳过这段数据。

猜你喜欢

转载自blog.csdn.net/fst438060684/article/details/82909771