当前位置:首页 > 开发 > 编程语言 > 网络编程 > 正文

netty源码分析之FrameDecoder(DelimiterBasedFrameDecoder)

发表于: 2013-02-02   作者:asialee   来源:转载   浏览次数:
摘要:           前面我们分析了FrameDecoder的实现,我们接下来看看它的子类实现吧!           我们首先来看DelimiterBasedFrameDecoder的实现,个人认为这个类实现的真的很牛,有些变量的含义作者没有增加注释,有时候可能不容易猜到意图。首先我们来看一下这个

          前面我们分析了FrameDecoder的实现,我们接下来看看它的子类实现吧!

          我们首先来看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;
    }

 

netty源码分析之FrameDecoder(DelimiterBasedFrameDecoder)

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
netty里面最重要的应该是ChannelHandler,这个里面也是用户编程直接打交道的接口,也是串行于Channe
netty里面最重要的应该是ChannelHandler,这个里面也是用户编程直接打交道的接口,也是串行于Channe
下文将剖析ServerBootstrap.bindAsync()方法所涉及到的流程。 1.概念 ChannelHandler分ChannelUpstre
先看下netty的channel对象关联关系。channel是由channelfactory来创建的,channelfactory又分为clie
先从EventExecutor开始,因为它是一个很基础的工具类,是对I/O线程的包装。先了解下它的源码会对后面
本文采用版本为Jboss Netty-3.2.4.Final,Jboss Netty示例example、几十页的user guide是快速学习的
本文为原创,转载请注明出处 netty 4源码分析-write Netty的写操作由两个步骤组成: Write:将msg存
看了两天的netty源码,现在总算有了些眉目了。下面是用excel画的一个简单的结构图 Channel是对最终I/
本文为原创,转载请注明出处 Netty4源码分析- read 当selector检测到OP_READ事件时,触发read操作:
参考: http://m.oschina.net/blog/527583?from=timeline&isappinstalled=1 https://lwn.net/Articl
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号