Netty源碼分析第六章: 解碼器html
第四節: 分隔符解碼器源碼分析
基於分隔符解碼器DelimiterBasedFrameDecoder, 是按照指定分隔符進行解碼的解碼器, 經過分隔符, 能夠將二進制流拆分紅完整的數據包this
一樣繼承了ByteToMessageDecoder並重寫了decode方法spa
咱們看其中的一個構造方法:指針
public DelimiterBasedFrameDecoder(int maxFrameLength, ByteBuf... delimiters) { this(maxFrameLength, true, delimiters); }
這裏參數maxFrameLength表明最大長度, delimiters是個可變參數, 能夠說能夠支持多個分隔符進行解碼code
咱們進入decode方法:orm
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方法並將解析好的數據添加到集合list中, 其父類就能夠遍歷out, 並將內容傳播htm
咱們跟到重載decode方法中:blog
protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception { //行處理器(1)
if (lineBasedDecoder != null) { return lineBasedDecoder.decode(ctx, buffer); } int minFrameLength = Integer.MAX_VALUE; ByteBuf minDelim = null; //找到最小長度的分隔符(2)
for (ByteBuf delim: delimiters) { //每一個分隔符分隔的數據包長度
int frameLength = indexOf(buffer, delim); if (frameLength >= 0 && frameLength < minFrameLength) { minFrameLength = frameLength; minDelim = delim; } } //解碼(3) //已經找到分隔符
if (minDelim != null) { int minDelimLength = minDelim.capacity(); ByteBuf frame; //當前分隔符否處於丟棄模式
if (discardingTooLongFrame) { //首先設置爲非丟棄模式
discardingTooLongFrame = false; //丟棄
buffer.skipBytes(minFrameLength + minDelimLength); int tooLongFrameLength = this.tooLongFrameLength; this.tooLongFrameLength = 0; if (!failFast) { fail(tooLongFrameLength); } return null; } //處於非丟棄模式 //當前找到的數據包, 大於容許的數據包
if (minFrameLength > maxFrameLength) { //當前數據包+最小分隔符長度 所有丟棄
buffer.skipBytes(minFrameLength + minDelimLength); //傳遞異常事件
fail(minFrameLength); return null; } //若是是正常的長度 //解析出來的數據包是否忽略分隔符
if (stripDelimiter) { //若是不包含分隔符 //截取
frame = buffer.readRetainedSlice(minFrameLength); //跳過度隔符
buffer.skipBytes(minDelimLength); } else { //截取包含分隔符的長度
frame = buffer.readRetainedSlice(minFrameLength + minDelimLength); } return frame; } else { //若是沒有找到分隔符 //非丟棄模式
if (!discardingTooLongFrame) { //可讀字節大於容許的解析出來的長度
if (buffer.readableBytes() > maxFrameLength) { //將這個長度記錄下
tooLongFrameLength = buffer.readableBytes(); //跳過這段長度
buffer.skipBytes(buffer.readableBytes()); //標記當前處於丟棄狀態
discardingTooLongFrame = true; if (failFast) { fail(tooLongFrameLength); } } } else { tooLongFrameLength += buffer.readableBytes(); buffer.skipBytes(buffer.readableBytes()); } return null; } }
這裏的方法也比較長, 這裏也經過拆分進行剖析繼承
(1). 行處理器
(2). 找到最小長度分隔符
(3). 解碼
首先看第一步行處理器:
if (lineBasedDecoder != null) { return lineBasedDecoder.decode(ctx, buffer); }
這裏首先判斷成員變量lineBasedDecoder是否爲空, 若是不爲空則直接調用lineBasedDecoder的decode的方法進行解碼, lineBasedDecoder實際上就是上一小節剖析的LineBasedFrameDecoder解碼器
這個成員變量, 會在分隔符是\n和\r\n的時候進行初始化
咱們看初始化該屬性的構造方法:
public DelimiterBasedFrameDecoder( int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters) { //代碼省略 //若是是基於行的分隔
if (isLineBased(delimiters) && !isSubclass()) { //初始化行處理器
lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast); this.delimiters = null; } else { //代碼省略
} //代碼省略
}
這裏isLineBased(delimiters)會判斷是不是基於行的分隔, 跟到isLineBased方法中:
private static boolean isLineBased(final ByteBuf[] delimiters) { //分隔符長度不爲2
if (delimiters.length != 2) { return false; } //拿到第一個分隔符
ByteBuf a = delimiters[0]; //拿到第二個分隔符
ByteBuf b = delimiters[1]; if (a.capacity() < b.capacity()) { a = delimiters[1]; b = delimiters[0]; } //確保a是/r/n分隔符, 確保b是/n分隔符
return a.capacity() == 2 && b.capacity() == 1
&& a.getByte(0) == '\r' && a.getByte(1) == '\n'
&& b.getByte(0) == '\n'; }
首先判斷長度等於2, 直接返回false
而後拿到第一個分隔符a和第二個分隔符b, 而後判斷a的第一個分隔符是否是\r, a的第二個分隔符是否是\n, b的第一個分隔符是否是\n, 若是都爲true, 則條件成立
咱們回到decode方法中, 看步驟2, 找到最小長度的分隔符:
這裏最小長度的分隔符, 意思就是從讀指針開始, 找到最近的分隔符
for (ByteBuf delim: delimiters) { //每一個分隔符分隔的數據包長度
int frameLength = indexOf(buffer, delim); if (frameLength >= 0 && frameLength < minFrameLength) { minFrameLength = frameLength; minDelim = delim; } }
這裏會遍歷全部的分隔符, 而後找到每一個分隔符到讀指針到數據包長度
而後經過if判斷, 找到長度最小的數據包的長度, 而後保存當前數據包的的分隔符, 以下圖:
6-4-1
這裏假設A和B同爲分隔符, A分隔符到讀指針的長度小於B分隔符到讀指針的長度, 這裏會找到最小的分隔符A, 分隔符的最小長度, 就readIndex到A的長度
咱們繼續看第3步, 解碼:
if (minDelim != null) 表示已經找到最小長度分隔符, 咱們繼續看if塊中的邏輯:
int minDelimLength = minDelim.capacity(); ByteBuf frame; if (discardingTooLongFrame) { discardingTooLongFrame = false; buffer.skipBytes(minFrameLength + minDelimLength); int tooLongFrameLength = this.tooLongFrameLength; this.tooLongFrameLength = 0; if (!failFast) { fail(tooLongFrameLength); } return null; } if (minFrameLength > maxFrameLength) { buffer.skipBytes(minFrameLength + minDelimLength); fail(minFrameLength); return null; } if (stripDelimiter) { frame = buffer.readRetainedSlice(minFrameLength); buffer.skipBytes(minDelimLength); } else { frame = buffer.readRetainedSlice(minFrameLength + minDelimLength); } return frame;
if (discardingTooLongFrame) 表示當前是否處於非丟棄模式, 若是是丟棄模式, 則進入if塊
由於第一個不是丟棄模式, 因此這裏先分析if塊後面的邏輯
if (minFrameLength > maxFrameLength) 這裏是判斷當前找到的數據包長度大於最大長度, 這裏的最大長度使咱們建立解碼器的時候設置的, 若是超過了最大長度, 就經過 buffer.skipBytes(minFrameLength + minDelimLength) 方式, 跳過數據包+分隔符的長度, 也就是將這部分數據進行徹底丟棄
繼續往下看, 若是長度不大最大容許長度, 則經過 if (stripDelimiter) 判斷解析的出來的數據包是否包含分隔符, 若是不包含分隔符, 則截取數據包的長度以後, 跳過度隔符
咱們再回頭看 if (discardingTooLongFrame) 中的if塊中的邏輯, 也就是丟棄模式:
首先將discardingTooLongFrame設置爲false, 標記非丟棄模式
而後經過 buffer.skipBytes(minFrameLength + minDelimLength) 將數據包+分隔符長度的字節數跳過, 也就是進行丟棄, 以後再進行拋出異常
分析完成了找到分隔符以後的丟棄模式非丟棄模式的邏輯處理, 咱們在分析沒找到分隔符的邏輯處理, 也就是 if (minDelim != null) 中的else塊:
if (!discardingTooLongFrame) { if (buffer.readableBytes() > maxFrameLength) { tooLongFrameLength = buffer.readableBytes(); buffer.skipBytes(buffer.readableBytes()); discardingTooLongFrame = true; if (failFast) { fail(tooLongFrameLength); } } } else { tooLongFrameLength += buffer.readableBytes(); buffer.skipBytes(buffer.readableBytes()); } return null;
首先經過 if (!discardingTooLongFrame) 判斷是否爲非丟棄模式, 若是是, 則進入if塊:
在if塊中, 首先經過 if (buffer.readableBytes() > maxFrameLength) 判斷當前可讀字節數是否大於最大容許的長度, 若是大於最大容許的長度, 則將可讀字節數設置到tooLongFrameLength的屬性中, 表明丟棄的字節數
而後經過 buffer.skipBytes(buffer.readableBytes()) 將累計器中全部的可讀字節進行丟棄
最後將discardingTooLongFrame設置爲true, 也就是丟棄模式, 以後拋出異常
若是 if (!discardingTooLongFrame) 爲false, 也就是當前處於丟棄模式, 則追加tooLongFrameLength也就是丟棄的字節數的長度, 並經過 buffer.skipBytes(buffer.readableBytes()) 將全部的字節繼續進行丟棄
以上就是分隔符解碼器的相關邏輯
第六章總結
本章介紹了抽象解碼器ByteToMessageDecoder, 和其餘幾個實現了ByteToMessageDecoder類的解碼器, 這個幾個解碼器邏輯都比較簡單, 同窗們能夠根據其中的思想剖析其餘的比較複雜的解碼器, 或者根據其規則實現本身的自定義解碼器