netty源码阅读之解码之行解码器分析

行解码器LineBasedFrameDecoder,我们首先看这个类的一些成员变量:

/**
 * A decoder that splits the received {@link ByteBuf}s on line endings.
 * <p>
 * Both {@code "\n"} and {@code "\r\n"} are handled.
 * For a more general delimiter-based decoder, see {@link DelimiterBasedFrameDecoder}.
 */
public class LineBasedFrameDecoder extends ByteToMessageDecoder {

    /** Maximum length of a frame we're willing to decode.  */
    private final int maxLength;
    /** Whether or not to throw an exception as soon as we exceed maxLength. */
    private final boolean failFast;
    private final boolean stripDelimiter;

    /** True if we're discarding input because we're already over maxLength.  */
    private boolean discarding;
    private int discardedBytes;

maxLength:行解码器最长解析的字符长度,超过这个长度将不解析。

failFast:当到达maxLength的时候,是否抛出异常,还是等到有换行符的时候才抛出异常。

stripDelimiter:解析字符串成对象的时候,是否把字符串也加入到对象中。

discarding:是否进入丢弃模式。

discardedBytes:需要丢弃的字节数。

在继续下面的分析之前,我们还要知道一个概念:

\n是换行符,\r\n也是换行符

现在开始分析。

它的decode方法和基于固定长度解码器一样,也是把解析到的对象放到out列表里面:

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

最重要的是它实现的deocode(ctx,in)方法:

    /**
     * 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 {
        final int eol = findEndOfLine(buffer);
        if (!discarding) {
            if (eol >= 0) {
                final ByteBuf frame;
                final int length = eol - buffer.readerIndex();
                final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;

                if (length > maxLength) {
                    buffer.readerIndex(eol + delimLength);
                    fail(ctx, length);
                    return null;
                }

                if (stripDelimiter) {
                    frame = buffer.readRetainedSlice(length);
                    buffer.skipBytes(delimLength);
                } else {
                    frame = buffer.readRetainedSlice(length + delimLength);
                }

                return frame;
            } else {
                final int length = buffer.readableBytes();
                if (length > maxLength) {
                    discardedBytes = length;
                    buffer.readerIndex(buffer.writerIndex());
                    discarding = true;
                    if (failFast) {
                        fail(ctx, "over " + discardedBytes);
                    }
                }
                return null;
            }
        } else {
            if (eol >= 0) {
                final int length = discardedBytes + eol - buffer.readerIndex();
                final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
                buffer.readerIndex(eol + delimLength);
                discardedBytes = 0;
                discarding = false;
                if (!failFast) {
                    fail(ctx, length);
                }
            } else {
                discardedBytes += buffer.readableBytes();
                buffer.readerIndex(buffer.writerIndex());
            }
            return null;
        }
    }

我们可以分为下面几点分析:

首先看final int eol = findEndOfLine(buffer);这个方法:

    private static int findEndOfLine(final ByteBuf buffer) {
        int i = buffer.forEachByte(ByteProcessor.FIND_LF);
        if (i > 0 && buffer.getByte(i - 1) == '\r') {
            i--;
        }
        return i;
    }

看ByteProcessor.FIND_LF是什么,其实就是一个换行符:

    ByteProcessor FIND_LF = new IndexOfProcessor((byte) '\n');

所以findEndOfLine做的事情很简单,就是找到\n换行符,如果前面有\r,就返回前一个。

下面就是分几种情况。

1、如果是非丢弃模式

    A、非丢弃模式下读到了换行符

    B、非丢弃模式下没有读到换行符

2、如果是丢弃模式

    A、丢弃模式下读到了换行符

    B、丢弃模式下没有读到换行符

我们一点一点分析,其实也很简单:

1、如果是非丢弃模式

一开始的时候肯定还没有进入丢弃模式,只有下面的一种情况里面的一种小情况才会进入丢弃模式,我们下面会分析。

    A、非丢弃模式下读到了换行符

看下面的图了解这种情况:

eol就是end of line,读到了换行符,有两种情况都属于。看源码:

                final ByteBuf frame;
                final int length = eol - buffer.readerIndex();
                final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;

length就是readIndex到eol的这段距离,而delimLength就是分隔符的长度,有\r就是2,没有就是1

然后,如果readIndex到eol的这段距离岛屿maxLength,也就是length>maxLength:

                if (length > maxLength) {
                    buffer.readerIndex(eol + delimLength);
                    fail(ctx, length);
                    return null;
                }

 那么我们就要抛弃length这一段加上delimLength这一段,也就是把readIndex变为eol+delimLength,然后fail来抛出异常,并且返回空。

继续:

                if (stripDelimiter) {
                    frame = buffer.readRetainedSlice(length);
                    buffer.skipBytes(delimLength);
                } else {
                    frame = buffer.readRetainedSlice(length + delimLength);
                }

                return frame;

走到上面这一段,说明这些解析到的数据是正常的了,通过stripDelimiter判断需不需要跳过跳过换行符,然后在返回得到的数据。

    B、非丢弃模式下没有读到换行符

看源码:

                final int length = buffer.readableBytes();
                if (length > maxLength) {
                    discardedBytes = length;
                    buffer.readerIndex(buffer.writerIndex());
                    discarding = true;
                    if (failFast) {
                        fail(ctx, "over " + discardedBytes);
                    }
                }
                return null;

这里无论如何都是返回 空的,因为没有读到换行符。到时如果读到的数据length岛屿maxLength,那么就开始进入丢弃模式了,把需要求其的字节个数记录下来,然后把readIndex移动到,witerIndex,也就是readIndex到writeIndex这一段都是要抛弃的,看这里

这段数据都不要。

设置为丢弃模式:discarding=true

如果是快速失败模式,那这个时候就可以fail来抛出异常了,否则,就等到下面读到换行符再调用fail方法。

2、如果是丢弃模式

    A、丢弃模式下读到了换行符

图还是用这个图就行了:

如果是这种情况,那么我们就要抛弃readIndex到eol这一段数据再加上之前记录的discardedBytes,并传递到fail函数里面,然后设置discardedBytes为0,并且设置丢弃模式结束,也就是false。如果非快速失败,这个时候就可以执行fail了。上面执行fail和这里执行fail是互斥事件,上面执行这里就不会执行,否则这里不执行上面就执行。看源码:

                final int length = discardedBytes + eol - buffer.readerIndex();
                final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
                buffer.readerIndex(eol + delimLength);
                discardedBytes = 0;
                discarding = false;
                if (!failFast) {
                    fail(ctx, length);
                }

    B、丢弃模式下没有读到换行符

这种情况比较简单,如果是丢弃模式,并且没有读到换行符,那就继续继续需要失败的字节,并抛弃这些字节,也就是把readIndex移动到wirteIndex这里

看源码:

                discardedBytes += buffer.readableBytes();
                buffer.readerIndex(buffer.writerIndex());

猜你喜欢

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