【Netty4.x】Netty原始碼分析(三)之LineBasedFrameDecoder
阿新 • • 發佈:2019-01-23
在上一篇:【遊戲開發】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.” +------------------------------------------------------------------+若有錯誤之處,請多多諒解並歡迎批評指正。 本部落格中未標明轉載的文章歸作者小毛驢所有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。