Netty服务端源码阅读笔记(六)服务端拆包粘包--LengthFieldBasedFrameDecoder

58 篇文章 0 订阅
16 篇文章 0 订阅

接上篇   Netty服务端源码阅读笔记(六)服务端拆包粘包和解决方法代码示例

代码不贴了,看上篇文章吧

客户端handler依次为LengthFieldPrepender用来为消息增加元数据头的handler和自定义的handler

服务端handler依次为LengthFieldBasedFrameDecoder、StringDecoder、和自定义的handler

目录

1、LengthFieldPrepender

1.1、MessageToMessageEncoder

1.2、new LengthFieldPrepender

1.2.1、ByteOrder 字节序

1.3、encode

2、LengthFieldBasedFrameDecoder

2.1、ByteToMessageDecoder

2.1.1、cumulator.cumulate(ctx.alloc(), cumulation, data);

2.1.2、callDecode

2.2 decode


 

1、LengthFieldPrepender

1.1、MessageToMessageEncoder<T>

LengthFieldPrepender继承MessageToMessageEncoder<ByteBuf>

熟悉的StringEncoder也继承这个类,不过泛型不一样,是MessageToMessageEncoder<CharSequence>,这个泛型标识了本handler只支持这个类型的消息处理,如果来的消息不是这个类型,会跳过本handler

public class LengthFieldPrepender extends MessageToMessageEncoder<ByteBuf> {
}
public abstract class MessageToMessageEncoder<I> extends ChannelOutboundHandlerAdapter {

    private final TypeParameterMatcher matcher;

    // new的时候初始化matcher,获取泛型类型class
    // find方法先忽略
    protected MessageToMessageEncoder() {
        matcher = TypeParameterMatcher.find(this, MessageToMessageEncoder.class, "I");
    }

    // 校验来的消息是不是泛型类型,内部就是判断泛型class.isInstance(msg)   
    public boolean acceptOutboundMessage(Object msg) throws Exception {
        return matcher.match(msg);
    }

    // 出栈处理器自然得实现write方法
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
       // 放置转换后的新对象的数据结构,新东西哈,再研究
        CodecOutputList out = null;
        try {
             // 判断是不是本handler需要处理得消息
            if (acceptOutboundMessage(msg)) {
                out = CodecOutputList.newInstance();
                @SuppressWarnings("unchecked")
                I cast = (I) msg;
                try {
                    // 抽象方法,子类实现
                    // 把原先的消息对象(cast)转换,把新对象放到out中
                    encode(ctx, cast, out);
                } finally {
                    // 释放原cast空间
                    // ReferenceCount引用计数,只有引用计数为0后才会释放空间
                    ReferenceCountUtil.release(cast);
                }

                if (out.isEmpty()) {
                    out.recycle();
                    out = null;

                    throw new EncoderException(
                            StringUtil.simpleClassName(this) + " must produce at least one message.");
                }
            } else {
               // 跳过本handler,开始调用链中上一个handler的write
                ctx.write(msg, promise);
            }
        } catch (EncoderException e) {
            throw e;
        } catch (Throwable t) {
            throw new EncoderException(t);
        } finally {
             // 如果转换成功并放到了out中
            if (out != null) {
                final int sizeMinusOne = out.size() - 1;
                if (sizeMinusOne == 0) {
                     //如果消息被转换后也是一条,直接获取并继续调用链handler    
                    ctx.write(out.getUnsafe(0), promise);
                } else if (sizeMinusOne > 0) {
                    // 如果消息被转换后成了多条,循环每条再调用链handler继续处理
                    if (promise == ctx.voidPromise()) {
                        writeVoidPromise(ctx, out);
                    } else {
                        // 如果ctx的promise对象不是write入参的promise,额外调用一个finish方法更新promise状态,为什么有这种情况还不知道
                        writePromiseCombiner(ctx, out, promise);
                    }
                }
                out.recycle();
            }
        }
    }


    private static void writeVoidPromise(ChannelHandlerContext ctx, CodecOutputList out) {
        final ChannelPromise voidPromise = ctx.voidPromise();
        for (int i = 0; i < out.size(); i++) {
            ctx.write(out.getUnsafe(i), voidPromise);
        }
    }

    private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, ChannelPromise promise) {
        final PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
        for (int i = 0; i < out.size(); i++) {
            combiner.add(ctx.write(out.getUnsafe(i)));
        }
        combiner.finish(promise);
    }
}

看过了父类,知道了这个LengthFieldPrepender只接受ByteBuf类型的消息,这也是我们客户端handler没有StringEncoder的原因,因为自定义的handler直接write的就是ByteBuf类型

那就剩看下这个类初始化和encode方法了

1.2、new LengthFieldPrepender

    public LengthFieldPrepender(
            ByteOrder byteOrder, int lengthFieldLength,
            int lengthAdjustment, boolean lengthIncludesLengthFieldLength) {
        
        // 构造函数中传入的长度段大小必须为1,2,3,4,8中的一个,表示长度段占几个字节位置
        if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
            lengthFieldLength != 3 && lengthFieldLength != 4 &&
            lengthFieldLength != 8) {
            throw new IllegalArgumentException(
                    "lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
                    lengthFieldLength);
        }
       
        ObjectUtil.checkNotNull(byteOrder, "byteOrder");

         // 字节序,默认大端序
        this.byteOrder = byteOrder;

         // 长度段的字节长度
        this.lengthFieldLength = lengthFieldLength;

         // 算消息总长度时是否把长度段那几个字节算进来,默认不算,false
        this.lengthIncludesLengthFieldLength = lengthIncludesLengthFieldLength;

         // 调整长度,看代码应该是 算消息总长的时候的保留长度,默认0
        this.lengthAdjustment = lengthAdjustment;
    }

1.2.1、ByteOrder 字节序

分大端序ByteOrder.BIG_ENDIAN 和小端序ByteOrder.LITTLE_ENDIAN,在读或写入多字节值的时候使用,java默认为大端序,用代码来解释:

	public static void main(String[] args) {
		System.out.println("5003二进制表示::" + Integer.toBinaryString(5003));
		
		ByteBuffer buf = ByteBuffer.allocate(10);
		System.out.println("当前字节序:" + buf.order().toString());
		buf.putInt(5003);
		buf.flip();
		for (int i = 0; i < buf.limit(); i++)
		    System.out.print((buf.get() & 0xFF) + ",");
		
		buf.clear();
		buf.order(ByteOrder.LITTLE_ENDIAN);
		System.out.println();
		System.out.println("当前字节序:" + buf.order().toString());
		buf.putInt(5003);
		buf.flip();
		for (int i = 0; i < buf.limit(); i++)
		    System.out.print((buf.get() & 0xFF) + ",");
	}

执行结果就一目了然

5003二进制表示::1001110001011
当前字节序:BIG_ENDIAN
0,0,19,139,
当前字节序:LITTLE_ENDIAN
139,19,0,0,

1.3、encode


    @Override
    protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        // 获取msg的总消息长度
        int length = msg.readableBytes() + lengthAdjustment;
        if (lengthIncludesLengthFieldLength) {
            // 如果还得算长度段,就把长度段加上
            length += lengthFieldLength;
        }

        if (length < 0) {
            throw new IllegalArgumentException(
                    "Adjusted frame length (" + length + ") is less than zero");
        }

        // 分配长度段空间,写入消息大小
        // 长度段大小,1个字节2^8 = 256,最大表示的消息长度为256字节,其他类似
        switch (lengthFieldLength) {
        case 1:
            if (length >= 256) {
                throw new IllegalArgumentException(
                        "length does not fit into a byte: " + length);
            }
            out.add(ctx.alloc().buffer(1).order(byteOrder).writeByte((byte) length));
            break;
        case 2:
            if (length >= 65536) {
                throw new IllegalArgumentException(
                        "length does not fit into a short integer: " + length);
            }
            out.add(ctx.alloc().buffer(2).order(byteOrder).writeShort((short) length));
            break;
        case 3:
            if (length >= 16777216) {
                throw new IllegalArgumentException(
                        "length does not fit into a medium integer: " + length);
            }
            out.add(ctx.alloc().buffer(3).order(byteOrder).writeMedium(length));
            break;
        case 4:
            out.add(ctx.alloc().buffer(4).order(byteOrder).writeInt(length));
            break;
        case 8:
            out.add(ctx.alloc().buffer(8).order(byteOrder).writeLong(length));
            break;
        default:
            throw new Error("should not reach here");
        }

        // 再把原消息体塞进去
        out.add(msg.retain());
    }

 

2、LengthFieldBasedFrameDecoder

public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {}

2.1、ByteToMessageDecoder

入栈处理器,自然是要看channelRead()方法

先大致了解思想:每次channel来ByteBuf消息,ByteToMessageDecoder中会有一个ByteBuf变量存放,如果没达到处理要求,则返回null,不在handler链上执行。下次继续来消息,会把这次的和上次的消息合并起来再去处理,从而达到处理要求之后,处理完毕执行handler链

public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {
 
    ByteBuf cumulation;
    private Cumulator cumulator = MERGE_CUMULATOR;

    private byte decodeState = STATE_INIT; // 当前处理状态,右下边三种状态
    private static final byte STATE_INIT = 0;// 初始化
    private static final byte STATE_CALLING_CHILD_DECODE = 1;// 正在调用子类decode方法
    private static final byte STATE_HANDLER_REMOVED_PENDING = 2;// 处理器即将被移除

    private int discardAfterReads = 16;
    private int numReads;

  @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 只处理ByteBuf类型的消息
        if (msg instanceof ByteBuf) {
            // 保存处理结果的集合list
            CodecOutputList out = CodecOutputList.newInstance();
            try {
                ByteBuf data = (ByteBuf) msg;

                // cumulation 是一个缓存的byteBuf
                // 如果本次还没读到指定长度暂时不会处理这个消息,本次读到的数据就放在这个byteBuf里边
                // 下次读的时候两次的会合并起来再去处理
                first = cumulation == null;

                if (first) {
                    cumulation = data;
                } else {
                    // cumulator.cumulate合并两次的byteBuf
                    cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
                }

                // 调用子类decode方法
                callDecode(ctx, cumulation, out);
            } catch (DecoderException e) {
                throw e;
            } catch (Exception e) {
                throw new DecoderException(e);
            } finally {
                // 如果cumulation不为空,且不可读,释放空间
                if (cumulation != null && !cumulation.isReadable()) {
                    numReads = 0;
                    cumulation.release();
                    cumulation = null;
                } else if (++ numReads >= discardAfterReads) {
                     // 超过最大读取次数16次默认,同AbstractNioByteChannel.NioByteUnsafe#read中每次do while循环最多16次
                    numReads = 0;
                    discardSomeReadBytes();
                }

                // 调用其他channelRead
                int size = out.size();
                decodeWasNull = !out.insertSinceRecycled();
                fireChannelRead(ctx, out, size);
                out.recycle();
            }
        } else {
            // 非ByteBuf类的消息直接跳过
            ctx.fireChannelRead(msg);
        }
    }

   static void fireChannelRead(ChannelHandlerContext ctx, List<Object> msgs, int numElements) {
        if (msgs instanceof CodecOutputList) {
            fireChannelRead(ctx, (CodecOutputList) msgs, numElements);
        } else {
            for (int i = 0; i < numElements; i++) {
                ctx.fireChannelRead(msgs.get(i));
            }
        }
    }

    /**
     * Get {@code numElements} out of the {@link CodecOutputList} and forward these through the pipeline.
     */
    static void fireChannelRead(ChannelHandlerContext ctx, CodecOutputList msgs, int numElements) {
        for (int i = 0; i < numElements; i ++) {
            ctx.fireChannelRead(msgs.getUnsafe(i));
        }
    }

    protected final void discardSomeReadBytes() {
        if (cumulation != null && !first && cumulation.refCnt() == 1) {

            // 把还未读取的数据挪到前边,丢弃其他部分,以避免额外内存带来的内存消耗
            cumulation.discardSomeReadBytes();
        }
    }
}

2.1.1、cumulator.cumulate(ctx.alloc(), cumulation, data);

    public static final Cumulator MERGE_CUMULATOR = new Cumulator() {
        @Override
        public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) {
            final ByteBuf buffer;
            if (cumulation.writerIndex() > cumulation.maxCapacity() - in.readableBytes()
                    || cumulation.refCnt() > 1 || cumulation.isReadOnly()) {
                // Expand cumulation (by replace it) when either there is not more room in the buffer
                // or if the refCnt is greater then 1 which may happen when the user use slice().retain() or
                // duplicate().retain() or if its read-only.
                // 1、写索引大于总容量-可读数  表示没有足够的空间来合并了,需要扩容
                // 2、引用数大于1(调用过slice().retain()或duplicate().retain()) 
                // 3、cumulation是只读的
                buffer = expandCumulation(alloc, cumulation, in.readableBytes());
            } else {
                buffer = cumulation;
            }

            // 合并
            buffer.writeBytes(in);
            // 释放原byteBuf
            in.release();
            return buffer;
        }
    };

    static ByteBuf expandCumulation(ByteBufAllocator alloc, ByteBuf cumulation, int readable) {
        ByteBuf oldCumulation = cumulation;
        // 重新分配一个足够大的
        cumulation = alloc.buffer(oldCumulation.readableBytes() + readable);

        // 从原来的buf复制到新buf上
        cumulation.writeBytes(oldCumulation);
        oldCumulation.release();
        return cumulation;
    }

2.1.2、callDecode

   protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
        try {
            while (in.isReadable()) {
                int outSize = out.size();

               // 如果已经decode完毕,out有值了,继续handler链的channelRead
                if (outSize > 0) {
                    fireChannelRead(ctx, out, outSize);
                    out.clear();

                    if (ctx.isRemoved()) {
                        break;
                    }
                    outSize = 0;
                }

                // 可读长度
                int oldInputLength = in.readableBytes();
                // 调用decode方法,处理结果会被放在out这个list中
                decodeRemovalReentryProtection(ctx, in, out);

                // 如果手动移除处理器了,跳出循环
                if (ctx.isRemoved()) {
                    break;
                }

                // outSize == 0 ,这里表示out没有解码出数据
                if (outSize == out.size()) {
                    // 子类并没有从in中读取任何数据,表示本次还没有读取条件
                    // 例如没有到指定长度,暂时不处理,等下次一块处理
                    if (oldInputLength == in.readableBytes()) {
                        break;
                    } else {
                        // 读取数据了,但是没有返回值,继续读取
                        continue;
                    }
                }

                // 这里out.size > 0 ,且没读任何数据,自定义的decode有问题,报个错
                if (oldInputLength == in.readableBytes()) {
                    throw new DecoderException(
                            StringUtil.simpleClassName(getClass()) +
                                    ".decode() did not read anything but decoded a message.");
                }

               // 这个应该是特殊解码用
                if (isSingleDecode()) {
                    break;
                }
            }
        } catch (DecoderException e) {
            throw e;
        } catch (Exception cause) {
            throw new DecoderException(cause);
        }
    }

    final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
            throws Exception {
        // 状态改为 调用子类decode
        decodeState = STATE_CALLING_CHILD_DECODE;
        try {
            // 子类实现,解码后的消息对象放到out中
            decode(ctx, in, out);
        } finally {
            // 是否要移除handler
            boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING;
            decodeState = STATE_INIT;
            if (removePending) {
                handlerRemoved(ctx);
            }
        }
    }

2.2 decode

public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {
    private final ByteOrder byteOrder;
    private final int maxFrameLength;
    private final int lengthFieldOffset;
    private final int lengthFieldLength;
    private final int lengthFieldEndOffset;
    private final int lengthAdjustment;
    private final int initialBytesToStrip;
    private final boolean failFast;
    private boolean discardingTooLongFrame;
    private long tooLongFrameLength;
    private long bytesToDiscard;

    public LengthFieldBasedFrameDecoder(
            ByteOrder byteOrder, int maxFrameLength, int lengthFieldOffset, int lengthFieldLength,
            int lengthAdjustment, int initialBytesToStrip, boolean failFast) {

        this.byteOrder = byteOrder;// 字节序,默认大端序
        this.maxFrameLength = maxFrameLength;// 需要解吗的段最大长度
        this.lengthFieldOffset = lengthFieldOffset;// 长度标识的偏移量
        this.lengthFieldLength = lengthFieldLength;// 长度标识的长度,1,2,3,4,8
        this.lengthAdjustment = lengthAdjustment;// 消息总长,预留长度
        lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;// 消息体开头的偏移量
        this.initialBytesToStrip = initialBytesToStrip;// 不知道,默认0
        this.failFast = failFast;// 默认true
    }


   @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 in) throws Exception {
        // 首次进来false
        if (discardingTooLongFrame) {
            discardingTooLongFrame(in);
        }

        // 消息的可读字节数还没有 长度段的长度 长,过了过了
        if (in.readableBytes() < lengthFieldEndOffset) {
            return null;
        }

        // 读索引 + 长度段的偏移量 才是真正的长度段偏移量
        int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;

        // 从in中 actualLengthFieldOffset 这个位置开始读lengthFieldLength这么长字节
        // 按byteOrder字节序读出实际消息的long长度
        long frameLength = getUnadjustedFrameLength(in, actualLengthFieldOffset, lengthFieldLength, byteOrder);

        // 报错!!先把in这个lengthFieldEndOffset位置前边的直接不看了跳过,然后报错
        if (frameLength < 0) {
            failOnNegativeLengthField(in, frameLength, lengthFieldEndOffset);
        }

        // 算算实际消息长度加上长度段一共多长(还有个调整长度,暂时可以忽略它)
        frameLength += lengthAdjustment + lengthFieldEndOffset;

        // 这什么情况??加起来还没长度段长,跳过offset长度然后报错报错
        if (frameLength < lengthFieldEndOffset) {
            failOnFrameLengthLessThanLengthFieldEndOffset(in, frameLength, lengthFieldEndOffset);
        }

        // 总长大于设置的 可操作最大Frame长度
        if (frameLength > maxFrameLength) {
            // 跳过这一段frameLength 不读了skip跳过
            exceededFrameLength(in, frameLength);
            return null;
        }

        // 可读字节数没够长度,暂时不读,先在父类攒着
        int frameLengthInt = (int) frameLength;
        if (in.readableBytes() < frameLengthInt) {
            return null;
        }

        // 这一步我不知道干啥的,initialBytesToStrip默认0,先忽略
        if (initialBytesToStrip > frameLengthInt) {
            failOnFrameLengthLessThanInitialBytesToStrip(in, frameLength, initialBytesToStrip);
        }
        in.skipBytes(initialBytesToStrip);

        // 总算攒够足够长的ByteBuf了,开始截取
        int readerIndex = in.readerIndex();
        int actualFrameLength = frameLengthInt - initialBytesToStrip;
        ByteBuf frame = extractFrame(ctx, in, readerIndex, actualFrameLength);
        in.readerIndex(readerIndex + actualFrameLength);
        return frame;
    }

    private static void failOnNegativeLengthField(ByteBuf in, long frameLength, int lengthFieldEndOffset) {
        in.skipBytes(lengthFieldEndOffset);
        throw new CorruptedFrameException(
           "negative pre-adjustment length field: " + frameLength);
    }

  // 太长了处理不了,送走,不处理
   private void exceededFrameLength(ByteBuf in, long frameLength) {
        // discard < 0 ,计算出的消息总长如果小于in中可读字节数,那就把frameLength忽略掉
        // discard > 0 ,本次in还不是全部消息,下次接进来的数据还得继续忽略
        long discard = frameLength - in.readableBytes();
        tooLongFrameLength = frameLength;

        if (discard < 0) {
            //直接忽略整个frameLength
            in.skipBytes((int) frameLength);
        } else {
            // discardingTooLongFrame 这就是下次进来需要继续忽略的标志
            // bytesToDiscard 下拨消息忽略多少字节  
            discardingTooLongFrame = true;
            bytesToDiscard = discard;
            in.skipBytes(in.readableBytes());
        }

        // 是否需要报错
        failIfNecessary(true);
    }

 private void failIfNecessary(boolean firstDetectionOfTooLongFrame) {
        // bytesToDiscard == 0 表示已经把要跳过的超长Frame跳过完了
        if (bytesToDiscard == 0) {
            // 标志位重置
            long tooLongFrameLength = this.tooLongFrameLength;
            this.tooLongFrameLength = 0;
            discardingTooLongFrame = false;

            // 这里firstDetectionOfTooLongFrame为false
            // failFast默认为true,表示发现超长即报错
            // 为false,要把本次超长的段全跳过skip,再报错
            if (!failFast || firstDetectionOfTooLongFrame) {
                fail(tooLongFrameLength);
            }
        } else {
            // failFast 默认为true,就是不等下一次来,直接本次发现超长就报错
            if (failFast && firstDetectionOfTooLongFrame) {
                fail(tooLongFrameLength);
            }
        }
    }

    // Slice()方法获取一个独立读写索引的ByteBuf,共享内存区域,retain引用加一
    protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) {
        return buffer.retainedSlice(index, length);
    }

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值