NETTY源碼學習-DELIMITERBASEDFRAMEDECODER

來源:互聯網
上載者:User

標籤:

看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\nmsg = 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;    }}

NETTY源碼學習-DELIMITERBASEDFRAMEDECODER

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.