Netty-源碼分析LineBasedFrameDecoder

LineBasedFrameDecoder源碼分析


package io.netty.handler.codec;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ByteProcessor;

import java.util.List;

//\r\n分割器,編碼採用ASCII或者UTF-8,因爲\r\n的ASCII,或UTF-8編碼都是一致的。
//都是0D,0A,再0x7F[0111 1111]範圍內,超過這個範圍的碼點採用DelimiterBasedFrameDecoder解碼
public class LineBasedFrameDecoder extends ByteToMessageDecoder {

    //每段消息的最大長度
    private final int maxLength;

    //超過最大長度是否拋出異常
    /** Whether or not to throw an exception as soon as we exceed maxLength. */
    private final boolean failFast = false;

    //是否跳過分隔符
    private final boolean stripDelimiter = true;

    //true的話需要丟棄一部分字節,因爲超過了每段消息的最大長度
    /** True if we're discarding input because we're already over maxLength.  */
    private boolean discarding;

    //丟棄字節的長度
    private int discardedBytes;

    //偏移量
    /** Last scan position. */
    private int offset;

    @Override
    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        //如果解析出decoded就放入out集合,父類會fire給下一個handler
        Object decoded = decode(ctx, in);
        if (decoded != null) {
            out.add(decoded);
        }
    }

    
    protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
        //查詢\r\n的位置 假設消息爲 "abcedf\r\n"  整體length=8  那麼eol=6 
        final int eol = findEndOfLine(buffer);

        //discarding=false 說明正常情況
        if (!discarding) {

            //查詢到\r\n的位置
            if (eol >= 0) {

                final ByteBuf frame;
                //6-0 = 6 是消息的長度
                final int length = eol - buffer.readerIndex();

                //eol的位置 如果是\r則分隔符2個字節, 否則一個字節。
                final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;

                //如果消息長度 大於 每段消息的最大長度限制
                if (length > maxLength) {

                    //把緩衝區readerIndex設置爲0,相當於丟棄了前面的字節數據。
                    buffer.readerIndex(eol + delimLength);
                    //fire異常其它handler
                    fail(ctx, length);
                    return null;
                }

                //正常情況

                //去掉分隔符
                if (stripDelimiter) {
                    //返回子緩衝區,原始緩衝區的readerIndex會往後遞增length個位置
                    //frame.refCnt=1    原始緩衝區的refCnt=2
                    //frame=[abcdef]
                    frame = buffer.readRetainedSlice(length);
                    //原始緩衝區丟棄delimLength長度的字節(丟棄分隔符)
                    buffer.skipBytes(delimLength);
                } else {
                    //不丟棄分隔符,那麼length需要在加上分隔符的長度 6+2=8
                    frame = buffer.readRetainedSlice(length + delimLength);
                }

                //返回子緩衝區
                return frame;
            } else {
                //沒找到分隔符位置

                //可讀字節長度
                final int length = buffer.readableBytes();

                //大於每段信息最大長度範圍
                if (length > maxLength) {

                    //記錄需要丟棄的字節數
                    discardedBytes = length;

                    //跳過length個字節
                    buffer.readerIndex(buffer.writerIndex());

                    //設置標記
                    discarding = true;
                    offset = 0;

                    //拋異常
                    if (failFast) {
                        fail(ctx, "over " + discardedBytes);
                    }
                }
                return null;
            }
        } else { //進到這裏,說明之前發生過特殊情況,需要丟棄一個消息

            //找到了分隔符
            if (eol >= 0) {

                //discardedBytes是之前記錄需要丟棄的字節數   [eol - buffer.readerIndex()]是分隔符前面的字節數
                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的值 然後繼續丟棄字節
                discardedBytes += buffer.readableBytes();
                buffer.readerIndex(buffer.writerIndex());
                // We skip everything in the buffer, we need to set the offset to 0 again.
                offset = 0;
            }
            return null;
        }
    }

    //fire異常信息
    private void fail(final ChannelHandlerContext ctx, int length) {
        fail(ctx, String.valueOf(length));
    }
    private void fail(final ChannelHandlerContext ctx, String length) {
        ctx.fireExceptionCaught(
                new TooLongFrameException(
                        "frame length (" + length + ") exceeds the allowed maximum (" + maxLength + ')'));
    }

    
    private int findEndOfLine(final ByteBuf buffer) {
        //buffer緩衝區中可讀字節數
        int totalLength = buffer.readableBytes();
        //從readerIndex+offset開始,到totalLength - offset截止,默認offset=0,查找\n的位置
        int i = buffer.forEachByte(buffer.readerIndex() + offset, totalLength - offset, ByteProcessor.FIND_LF);

        //找到
        if (i >= 0) {
            offset = 0;
            //看前一個字節是否是\r
            if (i > 0 && buffer.getByte(i - 1) == '\r') {
                //位置往前挪一位
                i--;
            }
        } else {
            //沒找到- offset等於當前可讀字節數
            offset = totalLength;
        }
        return i;
    }
}

 

發佈了175 篇原創文章 · 獲贊 70 · 訪問量 44萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章