看DelimiterBasedFrameDecoder的API,有舉例:
接收到的ChannelBuffer如下:
+--------------+ | ABC\nDEF\r\n | +--------------+
經過DelimiterBasedFrameDecoder(Delimiters.lineDelimiter())之后,得到:
+-----+-----+ | ABC | DEF | +-----+-----+
而不是
+----------+ | ABC\nDEF |
為什么 ?
首先要明確,如果不指定,DelimiterBasedFrameDecoder默認會去掉分隔符
其次看看Delimiters.lineDelimiter(),它返回兩組delimiter,分別對應
windows
和
linux
的換行符
???
public static ChannelBuffer[] lineDelimiter() { return new ChannelBuffer[] { ChannelBuffers.wrappedBuffer(new byte[] { '\r', '\n' }), ChannelBuffers.wrappedBuffer(new byte[] { '\n' }), }; }
考察這兩組分隔符
方案一
采用“\r\n”作為分隔,則返回
frameA = “ABC\nDEF”
方案二
采用“\n”返回
frameB_0 = “ABC”
frameB_1 = “DEF\r”
由于frameB_0的長度比frameA短,因此在這個例子中,會采用方案二
但有個問題,為什么不是比較全部,而是只比較frameB_0?
要知道,length(frameA) = length(frameB_0) + length(frameB_1),兩者相等
剛開始,我還以為跟split一樣,方案二會一次性返回“ABCDEF\r”
實際上不是
它是遇到一個分隔符,就返回一個結果
可以通過下面的代碼證明:
public class ClientHandler extends SimpleChannelUpstreamHandler { @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { String msg = "ABC\nDEF\r\n"; ChannelBuffer buff = ChannelBuffers.buffer(msg.length()); buff.writeBytes(msg.getBytes()); e.getChannel().write(buff); } }
Server:
??????
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
//這里設置:不刪除分隔符,方便觀察
pipeline.addLast("handler1", new DelimiterBasedFrameDecoder(8192, false, Delimiters.lineDelimiter()));
pipeline.addLast("handler2", new ServerStringHandler()); //
打印
decode后的結果
return pipeline;
}
});
ServerStringHandler:
public class ServerStringHandler extends SimpleChannelUpstreamHandler{ @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { ChannelBuffer buff = (ChannelBuffer)e.getMessage(); String msg = (String)buff.toString(Helper.CHARSET_UTF8); //String s = "abc\n"; 則msg_escape 會原樣輸出“abc\n”,而不是“abc”外加一個換行 String msg_escape = StringEscapeUtils.escapeJava(msg); System.out.println("msg = " + msg_escape); } }
結果ServerStringHandler會分兩次輸出:
msg = ABC\n msg = DEF\r\n
查看源碼,會更清楚:
public class DelimiterBasedFrameDecoder extends FrameDecoder { private final ChannelBuffer[] delimiters; private final int maxFrameLength; /*返回結果中,是否去掉分隔符 通常的調用是去掉分隔符,例如 new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()) 等價于 new DelimiterBasedFrameDecoder(8192, /*stripDelimiter=*/true, Delimiters.lineDelimiter()) */ private final boolean stripDelimiter; @Override protected Object decode( ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { // Try all delimiters and choose the delimiter which yields the shortest frame. int minFrameLength = Integer.MAX_VALUE; ChannelBuffer minDelim = null; /*迭代每一個delimiter,都嘗試進行decode, 然后選擇返回“shortest frame”的那個delimiter 重點在indexOf這個方法 */ for (ChannelBuffer delim: delimiters) { int frameLength = indexOf(buffer, delim); if (frameLength >= 0 && frameLength < minFrameLength) { minFrameLength = frameLength; minDelim = delim; } } if (minDelim != null) { int minDelimLength = minDelim.capacity(); ChannelBuffer frame; if (stripDelimiter) { frame = buffer.readBytes(minFrameLength); buffer.skipBytes(minDelimLength); } else { frame = buffer.readBytes(minFrameLength + minDelimLength); } return frame; } } /* 對frame(haystack)進行搜索,找到第一個delimiter(needle),這個位置記為i 返回 (i - haystack.readerIndex),也就是分隔后第一個sub frame的長度 可以看到,它是“找到一個,就返回一個” */ private static int indexOf(ChannelBuffer haystack, ChannelBuffer needle) { //遍歷haystack的每一個字節 for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) { int haystackIndex = i; int needleIndex; /*haystack是否出現了delimiter,注意delimiter是一個ChannelBuffer(byte[]) 例如對于haystack="ABC\r\nDEF",needle="\r\n" 那么當haystackIndex=3時,找到了“\r”,此時needleIndex=0 繼續執行循環,haystackIndex++,needleIndex++, 找到了“\n” 至此,整個needle都匹配到了 程序然后執行到if (needleIndex == needle.capacity()),返回結果 */ for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) { if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) { break; } else { haystackIndex ++; if (haystackIndex == haystack.writerIndex() && needleIndex != needle.capacity() - 1) { return -1; } } } if (needleIndex == needle.capacity()) { // Found the needle from the haystack! return i - haystack.readerIndex(); } } return -1; } }
================================================
轉載點別的
? ? 我們首先來看DelimiterBasedFrameDecoder的實現,個人認為這個類實現的真的很牛,有些變量的含義作者沒有增加注釋,有時候可能不容易猜到意圖。首先我們來看一下這個類的成員變量:
?
- private? final?ChannelBuffer[]?delimiters;??
- ??? private? final? int?maxFrameLength;??
- ??? private? final? boolean?stripDelimiter;??
- ??? private? final? boolean?failFast;??
- ??? private? boolean?discardingTooLongFrame;??
- ??? private? int?tooLongFrameLength;??
?
- delimiters比較好理解,應該就是這個可以接受多個分割符
- maxFrameLength這個是最大幀的length
- stripDelimiter這個也很好理解,是否跳過分隔符,就是最終解碼的數據里面是否包含分隔符
- failFast 為true是說發現讀到的數據已經超過了maxFrameLength了,立即報TooLongFrameException,如果為false就是讀完整個幀數據后再報
- discardingTooLongFrame 這個是最難理解的,含義是當前的解碼器是否處于discardingTooLongFrame狀態,這個參數最容易理解為是否丟棄tooLongFrame,這個是一個標志位,在構造函數里面是不能進行設置的,只能是解碼器進行設置
- tooLongFrameLength這個也是一個狀態屬性,就是說出現了超長幀了,哪這個幀的長度到底是多少,就是這個長度,一般來說是在發現當前buffer的可讀數據超過最大幀時候進行設置
? ? ? ??好,看完這個東東后我們就來看一下它的實現:
? ? ? ?我們就來看一下最長的這個構造函數吧:
? ? ? ??
- public?DelimiterBasedFrameDecoder(??
- ??????????? int?maxFrameLength,? boolean?stripDelimiter,? boolean?failFast,?ChannelBuffer...?delimiters)?{??
- ???????validateMaxFrameLength(maxFrameLength);??
- ??????? if?(delimiters?==? null)?{??
- ??????????? throw? new?NullPointerException( "delimiters");??
- ???????}??
- ??????? if?(delimiters.length?==? 0)?{??
- ??????????? throw? new?IllegalArgumentException( "empty?delimiters");??
- ???????}??
- ??????? this.delimiters?=? new?ChannelBuffer[delimiters.length];??
- ??????? for?( int?i?=? 0;?i?<?delimiters.length;?i?++)?{??
- ???????????ChannelBuffer?d?=?delimiters[i];??
- ???????????validateDelimiter(d);??
- ??????????? this.delimiters[i]?=?d.slice(d.readerIndex(),?d.readableBytes());??
- ???????}??
- ??????? this.maxFrameLength?=?maxFrameLength;??
- ??????? this.stripDelimiter?=?stripDelimiter;??
- ??????? this.failFast?=?failFast;??
- ???}??
? ??? ? 這個里面我們發現就是如果傳遞多個delimiter的時候,在這個進行了一個slice操作,沒有什么特別的。
? ? ? ?下來我們來看一下最關鍵的decode方法吧:
? ? ? ??
- @Override??
- ???? protected?Object?decode(??
- ????????????ChannelHandlerContext?ctx,?Channel?channel,?ChannelBuffer?buffer)? throws?Exception?{??
- ???????? //?Try?all?delimiters?and?choose?the?delimiter?which?yields?the?shortest?frame.??
- ???????? int?minFrameLength?=?Integer.MAX_VALUE;??
- ????????ChannelBuffer?minDelim?=? null;??
- ???????? for?(ChannelBuffer?delim:?delimiters)?{??
- ???????????? int?frameLength?=?indexOf(buffer,?delim);??
- ???????????? if?(frameLength?>=? 0?&&?frameLength?<?minFrameLength)?{??
- ????????????????minFrameLength?=?frameLength;??
- ????????????????minDelim?=?delim;??
- ????????????}??
- ????????}??
- ??
- ???????? if?(minDelim?!=? null)?{??
- ???????????? int?minDelimLength?=?minDelim.capacity();??
- ????????????ChannelBuffer?frame;??
- ??
- ???????????? if?(discardingTooLongFrame)?{??
- ???????????????? //?We've?just?finished?discarding?a?very?large?frame.??
- ???????????????? //?Go?back?to?the?initial?state.??
- ????????????????discardingTooLongFrame?=? false;??
- ????????????????buffer.skipBytes(minFrameLength?+?minDelimLength);??
- ??
- ???????????????? int?tooLongFrameLength?=? this.tooLongFrameLength;??
- ???????????????? this.tooLongFrameLength?=? 0;??
- ???????????????? if?(!failFast)?{??
- ????????????????????fail(ctx,?tooLongFrameLength);??
- ????????????????}??
- ???????????????? return? null;??
- ????????????}??
- ??
- ???????????? if?(minFrameLength?>?maxFrameLength)?{??
- ???????????????? //?Discard?read?frame.??
- ????????????????buffer.skipBytes(minFrameLength?+?minDelimLength);??
- ????????????????fail(ctx,?minFrameLength);??
- ???????????????? return? null;??
- ????????????}??
- ??
- ???????????? if?(stripDelimiter)?{??
- ????????????????frame?=?buffer.readBytes(minFrameLength);??
- ????????????????buffer.skipBytes(minDelimLength);??
- ????????????}? else?{??
- ????????????????frame?=?buffer.readBytes(minFrameLength?+?minDelimLength);??
- ????????????}??
- ??
- ???????????? return?frame;??
- ????????}? else?{??
- ???????????? if?(!discardingTooLongFrame)?{??
- ???????????????? if?(buffer.readableBytes()?>?maxFrameLength)?{??
- ???????????????????? //?Discard?the?content?of?the?buffer?until?a?delimiter?is?found.??
- ????????????????????tooLongFrameLength?=?buffer.readableBytes();??
- ????????????????????buffer.skipBytes(buffer.readableBytes());??
- ????????????????????discardingTooLongFrame?=? true;??
- ???????????????????? if?(failFast)?{??
- ????????????????????????fail(ctx,?tooLongFrameLength);??
- ????????????????????}??
- ????????????????}??
- ????????????}? else?{??
- ???????????????? //?Still?discarding?the?buffer?since?a?delimiter?is?not?found.??
- ????????????????tooLongFrameLength?+=?buffer.readableBytes();??
- ????????????????buffer.skipBytes(buffer.readableBytes());??
- ????????????}??
- ???????????? return? null;??
- ????????}??
- ????}??
? ? ? ? ??我們慢慢的來看這個代碼的實現,作者為了實現failfast做了很多努力。首先我們看到最開始的代碼就發現,最上面實際上是嘗試所有的分隔符,然后找出一個可用將幀分割最小的一個分割符出來,下面就是if和else,我們先來看if的邏輯:
- ? 如果找到了分割符,如果當前的解碼器處于discardingTooLongFrame狀態,也就是說上次解碼的時候發現了超長幀,被拋棄過。首先將這個狀態修改過來,標志為不是拋棄過超長幀,這個時候將整個幀丟棄掉,然后如果不是failfast狀態,拋出異常,這個怎么理解呢,可以理解為上次在讀取的時候發現了超長幀,但是由于設置了不立即拋出異常,而是等讀完整個幀數據才拋出異常,這個時候既然發現了分隔符,該到拋出異常的時候了,最后return null表明此次分幀是失敗狀態。
- 如果發現此次的幀數據超過最大幀的長度,直接拋出異常
- 最后就是如果跳過分隔符,就直接跳過,負責就和分隔符和幀的實際數據一塊返回
? ? ??else的邏輯是當前讀到的數據沒有發現分隔符的情況下的邏輯,我們來看下:
- ?如果發現當前的解碼器不是處于discardingTooLongFrame狀態,當前buffer里面的可讀數據又比最大幀要大,我們就將解碼器標記為discardingTooLongFrame狀態,并設置這個超長幀的大小,如果是failfast狀態,就立馬拋出異常,也就是說我們發現了超長幀了,所以我們立馬拋出異常
- 如果發現當前的解碼器已經處于discardingTooLongFrame狀態,我們別無他方,只能修改下tooLongFrameLength的長度,然后聽天由命,等待下次解碼操作
- 這個時候如果一旦發現了超長幀,都return null,含義就是說此次解碼是無效的
? ? ? ? 最后我們來看一下indexOf的實現吧,這個很簡單,其實思想和在字符串中找子串的思想是一致的,就不多講解了,自己上代碼:
? ? ? ? ?
- private? static? int?indexOf(ChannelBuffer?haystack,?ChannelBuffer?needle)?{??
- ???????? for?( int?i?=?haystack.readerIndex();?i?<?haystack.writerIndex();?i?++)?{??
- ???????????? int?haystackIndex?=?i;??
- ???????????? int?needleIndex;??
- ???????????? for?(needleIndex?=? 0;?needleIndex?<?needle.capacity();?needleIndex?++)?{??
- ???????????????? if?(haystack.getByte(haystackIndex)?!=?needle.getByte(needleIndex))?{??
- ???????????????????? break;??
- ????????????????}? else?{??
- ????????????????????haystackIndex?++;??
- ???????????????????? if?(haystackIndex?==?haystack.writerIndex()?&&??
- ????????????????????????needleIndex?!=?needle.capacity()?-? 1)?{??
- ???????????????????????? return?- 1;??
- ????????????????????}??
- ????????????????}??
- ????????????}??
- ??
- ???????????? if?(needleIndex?==?needle.capacity())?{??
- ???????????????? //?Found?the?needle?from?the?haystack!??
- ???????????????? return?i?-?haystack.readerIndex();??
- ????????????}??
- ????????}??
- ???????? return?- 1;??
- ????} ?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
