Netty-源码分析DelimiterBasedFrameDecoder

Posted 征服.刘华强

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty-源码分析DelimiterBasedFrameDecoder相关的知识,希望对你有一定的参考价值。

DelimiterBasedFrameDecoder自定义分隔符解码器

 


package io.netty.handler.codec;

import static io.netty.util.internal.ObjectUtil.checkPositive;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;

import java.util.List;


//自定义分隔符解码器
public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder 

	//自定义的分隔符,可以使用多个
    private final ByteBuf[] delimiters;

    //每个消息段落最大长度
    private final int maxFrameLength;

    //解码消息时,是否丢弃分隔符
    private final boolean stripDelimiter;

    //遇到错误时,是否抛出异常
    private final boolean failFast;

    //状态变量-是否正在丢弃一个段的消息
    private boolean discardingTooLongFrame;

    //丢弃总长度
    private int tooLongFrameLength;

    //\\r\\n的解码器
    private final LineBasedFrameDecoder lineBasedDecoder;

    
    public DelimiterBasedFrameDecoder(
            int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf... delimiters) 
        validateMaxFrameLength(maxFrameLength);
        if (delimiters == null) 
            throw new NullPointerException("delimiters");
        
        if (delimiters.length == 0) 
            throw new IllegalArgumentException("empty delimiters");
        

        //delimiters里的分隔符 是否是\\r\\n
        if (isLineBased(delimiters) && !isSubclass()) 
            lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
            this.delimiters = null;
         else 
        	//创建数组
            this.delimiters = new ByteBuf[delimiters.length];
            for (int i = 0; i < delimiters.length; i ++) 
                ByteBuf d = delimiters[i];
                validateDelimiter(d);
                //创建d的分区放入数组
                this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes());
            
            lineBasedDecoder = null;
        
        this.maxFrameLength = maxFrameLength;
        this.stripDelimiter = stripDelimiter;
        this.failFast = failFast;
    

    //判断是否是Delimiters.lineDelimiter()
    /** Returns true if the delimiters are "\\n" and "\\r\\n".  */
    private static boolean isLineBased(final ByteBuf[] delimiters) 
        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];
        
        return a.capacity() == 2 && b.capacity() == 1
                && a.getByte(0) == '\\r' && a.getByte(1) == '\\n'
                && b.getByte(0) == '\\n';
    

    //判断是否是子类
    private boolean isSubclass() 
        return getClass() != DelimiterBasedFrameDecoder.class;
    

    @Override
    protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception 
        Object decoded = decode(ctx, in);
        if (decoded != null) 
            out.add(decoded);
        
    

    
    protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception 
    	//如果分隔符是\\r\\n则用lineBasedDecoder解码器解析
    	//此类就相当于lineBasedDecoder
        if (lineBasedDecoder != null) 
            return lineBasedDecoder.decode(ctx, buffer);
        

        int minFrameLength = Integer.MAX_VALUE;
        ByteBuf minDelim = null;

        //循环所有分隔符,挨个去匹配
        for (ByteBuf delim: delimiters) 
        	//匹配分隔符的起始位置
            int frameLength = indexOf(buffer, delim);

            //大于0,并且小于int最大值 说明匹配成功
            if (frameLength >= 0 && frameLength < minFrameLength) 
                minFrameLength = frameLength;
                minDelim = delim;
            
        

        //说明匹配成功
        if (minDelim != null) 
        	//分隔符的capacity就代表了分隔符的长度
            int minDelimLength = minDelim.capacity();
            ByteBuf frame;

            //如果之前已经开始丢弃字节
            if (discardingTooLongFrame) 
                // We've just finished discarding a very large frame.
                // Go back to the initial state.
                //恢复标记位
                discardingTooLongFrame = false;
                //跳过minFrameLength+分隔符的长度,表示丢弃了前一个完整的消息
                buffer.skipBytes(minFrameLength + minDelimLength);

                //丢弃的字节总数
                int tooLongFrameLength = this.tooLongFrameLength;
                this.tooLongFrameLength = 0;
                if (!failFast) 
                    fail(tooLongFrameLength);
                
                return null;
            

            //消息长度大于阈值
            if (minFrameLength > maxFrameLength) 
                // Discard read frame.
                //丢弃一个消息
                buffer.skipBytes(minFrameLength + minDelimLength);
                fail(minFrameLength);
                return null;
            

            //丢弃分隔符
            if (stripDelimiter) 
            	//返回一个消息frame
                frame = buffer.readRetainedSlice(minFrameLength);
                //丢弃分隔符
                buffer.skipBytes(minDelimLength);
             else 
            	//返回一个消息,包括了分隔符
                frame = buffer.readRetainedSlice(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(tooLongFrameLength);
                    
                
             else 
            	//之前discardingTooLongFrame已经设置为true
                // Still discarding the buffer since a delimiter is not found.
                //增加总丢弃数量
                tooLongFrameLength += buffer.readableBytes();
                //继续丢弃字节
                buffer.skipBytes(buffer.readableBytes());
            
            return null;
        
    

    private void fail(long frameLength) 
        if (frameLength > 0) 
            throw new TooLongFrameException(
                            "frame length exceeds " + maxFrameLength +
                            ": " + frameLength + " - discarded");
         else 
            throw new TooLongFrameException(
                            "frame length exceeds " + maxFrameLength +
                            " - discarding");
        
    

    
    private static int indexOf(ByteBuf haystack, ByteBuf needle) 
    	//循环输入的字节流缓冲区,假设  a b c d e
        for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) 
        	//从[a] 0位置开始找
            int haystackIndex = i;

            int needleIndex;
            //循环分隔符
            for (needleIndex = 0; needleIndex < needle.capacity(); needleIndex ++) 
            	//如果分隔符与haystack.getByte(haystackIndex)不同,则结束当前循环
            	//haystack.getByte(haystackIndex) 从b开始继续查询
                if (haystack.getByte(haystackIndex) != needle.getByte(needleIndex)) 
                    break;
                 else 
                    haystackIndex ++;
                    //依次拿出bcde进行匹配,如果输入缓冲区到达末尾,并且分隔符缓冲区还没有到达末尾,说明匹配失败
                    if (haystackIndex == haystack.writerIndex() &&
                        needleIndex != needle.capacity() - 1) 
                        return -1;
                    
                
            

            //如果分隔符缓冲区达到末尾,还没发生上面的情况,说明匹配到了
            if (needleIndex == needle.capacity()) 
                // Found the needle from the haystack!
                //i减去haystack.readerIndex()的位置就是分隔符的位置
                return i - haystack.readerIndex();
            
        
        return -1;
    


 

以上是关于Netty-源码分析DelimiterBasedFrameDecoder的主要内容,如果未能解决你的问题,请参考以下文章

源码分析Netty4专栏

源码分析Netty4专栏

Netty-源码分析LineBasedFrameDecoder

Netty源码分析:read

Netty源码分析:read

[Netty源码分析]ByteBuf(一)