Netty源碼分析第6章(解碼器)---->第3節: 行解碼器

 

Netty源碼分析第六章: 解碼器html

 

第三節: 行解碼器源碼分析

 

這一小節瞭解下行解碼器LineBasedFrameDecoder, 行解碼器的功能是一個字節流, 以\r\n或者直接以\n結尾進行解碼, 也就是以換行符爲分隔進行解析spa

一樣, 這個解碼器也繼承了ByteToMessageDecoder指針

首先看其參數:code

//數據包的最大長度, 超過該長度會進行丟棄模式
private final int maxLength; //超出最大長度是否要拋出異常
private final boolean failFast; //最終解析的數據包是否帶有換行符
private final boolean stripDelimiter; //爲true說明當前解碼過程爲丟棄模式
private boolean discarding; //丟棄了多少字節
private int discardedBytes;

其中的丟棄模式, 咱們會在源碼中看到其中的含義htm

咱們看其decode方法:blog

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方法同樣, 調用重載的decode方法, 並將解碼後的內容放到out集合中繼承

咱們跟到重載的decode方法中:索引

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(); //拿到分隔符長度, 若是是\r\n結尾, 分隔符長度爲2
            final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1; //若是長度大於最大長度
            if (length > maxLength) { //指向換行符以後的可讀字節(這段數據徹底丟棄)
                buffer.readerIndex(eol + delimLength); //傳播異常事件
 fail(ctx, length); return null; } //若是此次解析的數據是有效的 //分隔符是否算在完整數據包裏 //true爲丟棄分隔符
            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); //當前丟棄的字節爲0
            discardedBytes = 0; //設置爲未丟棄模式
            discarding = false; //丟棄完字節以後觸發異常
            if (!failFast) { fail(ctx, length); } } else { //累計已丟棄的字節個數+當前可讀的長度
            discardedBytes += buffer.readableBytes(); //移動
 buffer.readerIndex(buffer.writerIndex()); } return null; } }

 final int eol = findEndOfLine(buffer) 這裏是找當前行的結尾的索引值, 也就是\r\n或者是\n:事件

 

 

6-3-1

圖中不難看出, 若是是以\n結尾的, 返回的索引值是\n的索引值, 若是是\r\n結尾的, 返回的索引值是\r的索引值

咱們看findEndOfLine(buffer)方法:

private static int findEndOfLine(final ByteBuf buffer) { //找到/n這個字節
    int i = buffer.forEachByte(ByteProcessor.FIND_LF); //若是找到了, 而且前面的字符是-r, 則指向/r字節
    if (i > 0 && buffer.getByte(i - 1) == '\r') { i--; } return i; }

這裏經過一個forEachByte方法找\n這個字節, 若是找到了, 而且前面是\r, 則返回\r的索引, 不然返回\n的索引

回到重載的decode方法中:

 if (!discarding) 判斷是否爲非丟棄模式, 默認是就是非丟棄模式, 因此進入if中

 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;

首先得到換行符到可讀字節之間的長度, 而後拿到換行符的長度, 若是是\n結尾, 那麼長度爲1, 若是是\r結尾, 長度爲2

 if (length > maxLength) 帶表若是長度超過最大長度, 則直接經過 readerIndex(eol + delimLength) 這種方式, 將讀指針指向換行符以後的字節, 說明換行符以前的字節須要徹底丟棄

6-3-2

丟棄以後經過fail方法傳播異常, 並返回null

繼續往下看, 走到下一步, 說明解析出來的數據長度沒有超過最大長度, 說明是有效數據包

 if (stripDelimiter) 表示是否要將分隔符放在完整數據包裏面, 若是是true, 則說明要丟棄分隔符, 而後截取有效長度, 並跳過度隔符長度

將包含分隔符進行截取

以上就是非丟棄模式下找到換行符的相關邏輯

咱們再看非丟棄模式下沒有找到換行符的相關邏輯, 也就是非丟棄模式下,  if (eol >= 0) 中的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;

首先經過 final int length = buffer.readableBytes() 獲取全部的可讀字節數

而後判斷可讀字節數是否超過了最大值, 若是超過最大值, 則屬性discardedBytes標記爲這個長度, 表明這段內容要進行丟棄

6-3-3

 buffer.readerIndex(buffer.writerIndex()) 這裏直接將讀指針移動到寫指針, 而且將discarding設置爲true, 就是丟棄模式

若是可讀字節沒有超過最大長度, 則返回null, 表示什麼都沒解析出來, 等着下次解析

咱們再看丟棄模式的處理邏輯, 也就是 if (!discarding) 中的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); }

若是找到換行符, 則須要將換行符以前的數據所有丟棄掉

6-3-4

 final int length = discardedBytes + eol - buffer.readerIndex() 這裏得到丟棄的字節總數, 也就是以前丟棄的字節數+如今須要丟棄的字節數

而後計算換行符的長度, 若是是\n則是1, \r\n就是2

 buffer.readerIndex(eol + delimLength) 這裏將讀指針移動到換行符以後的位置

而後將discarding設置爲false, 表示當前是非丟棄狀態

咱們再看丟棄模式未找到換行符的狀況, 也就是丟棄模式下,  if (eol >= 0) 中的else塊:

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

這裏作的事情很是簡單, 就是累計丟棄的字節數, 並將讀指針移動到寫指針, 也就是將數據所有丟棄

 

最後在丟棄模式下, decode方法返回null, 表明本次沒有解析出任何數據

以上就是行解碼器的相關邏輯

 

上一節: 固定長度解碼器

下一節: 分隔符解碼器

相關文章
相關標籤/搜索