1. 程式人生 > >【Netty4.x】Netty原始碼分析(三)之LineBasedFrameDecoder

【Netty4.x】Netty原始碼分析(三)之LineBasedFrameDecoder

   在上一篇:【遊戲開發】TCP粘包/拆包問題的解決辦法(二)文章中,我們在給ServerHandler之前添加了2個解碼器LineBasedFrameDecoder和StringDecoder解決了伺服器端粘包問題。今天我們就從原始碼上來分析LineBasedFrameDecoder。

一、LineBasedFrameDecoder

   1.1 工作原理

  LineBasedFrameDecoder是回車換行解碼器,如果使用者傳送的訊息以回車換行符作為訊息結束的標識,則可以直接使用Netty的LineBasedFrameDecoder對訊息進行解碼。它會依次遍歷ByteBuf中的可讀位元組,判斷看是否有“\n”或者“\r\n”,如果有,就以此位置為結束位置,從可讀索引到結束位置區間的位元組就組成了一行。它是以換行符為結束標誌的解碼器,支援攜帶結束符或者不攜帶結束符兩種解碼方式,同時支援配置單行的最大長度。如果連續讀取到最大長度後仍然沒有發現換行符,就會丟擲異常,同時忽略掉之前讀到的異常碼流。防止由於資料報沒有攜帶換行符導致接收到ByteBuf無限制積壓,引起系統記憶體溢位。

   1.2 變數解析

/** 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如果設定成true,當發現解析的資料超過maxLenght就立馬報錯,否則當整個幀的資料解析完後才報錯
stripDelimiter解碼時是否去掉分隔符
discarding如果沒有找到分隔符且讀取的資料超過最大長度,我們將將其設定為true
discardedBytes從原始碼上分析它,見下

   1.3 核心程式碼

 protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
	    //先找到結束符索引
        final int eol = findEndOfLine(buffer);
        if (!discarding) {
        	 //discarding預設為false,第一次必走這
            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 {
            	//沒有找到結束符索引,通過ByteBuf.readableBytes()方法獲取當前訊息的長度
                final int length = buffer.readableBytes();
                //如果長度超過最大長度
                if (length > maxLength) {
                   //使用discardedBytes臨時儲存訊息的長度,並將discarding設定為true
                    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;
        }
    }

   1.4 使用效果

解碼前:
+------------------------------------------------------------------+
                        接收到的資料報
“ Hello Netty.\r\n Are you ok?”
+------------------------------------------------------------------+
解碼後:
+------------------------------------------------------------------+
                        解碼之後的文字訊息
“Hello Netty.”
+------------------------------------------------------------------+
若有錯誤之處,請多多諒解並歡迎批評指正。 本部落格中未標明轉載的文章歸作者小毛驢所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。