Netty源码解析之ByteToMessageDecoder

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/heroqiang/article/details/81510059

阅读须知

  • Netty版本:4.1.14.Final
  • 文章中使用/* */注释的方法会做深入分析

正文

之前我们介绍了ChannelHandler,这篇文章我们来介绍一个具体的子类实现ByteToMessageDecoder,它是一个抽象类,继承了ChannelInboundHandlerAdapter,所以它处理入站事件,从命名上可以看出,它是一个解码器,用于将ByteBuf解码成POJO对象,我们来看实现:
ByteToMessageDecoder:

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    // 只处理ByteBuf类型的msg,其他透传
    if (msg instanceof ByteBuf) {
        CodecOutputList out = CodecOutputList.newInstance();
        try {
            ByteBuf data = (ByteBuf) msg;
            // 通过cumulation是否为空判断解码器是否缓存了没有解码完成的半包消息
            // 如果为空说明是首次解码或者最近一次已经处理完了半包消息
            first = cumulation == null;
            if (first) {
                cumulation = data; // cumulation为空直接赋值
            } else {
                /* cumulation不为空需要累积本次消息 */
                cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data);
            }
            /* 调用解码方法 */
            callDecode(ctx, cumulation, out);
        } catch (DecoderException e) {
            throw e;
        } catch (Throwable t) {
            throw new DecoderException(t);
        } finally {
            // 判断如果cumulation不为空并且已经读取完毕,则释放cumulation
            if (cumulation != null && !cumulation.isReadable()) {
                numReads = 0;
                cumulation.release();
                cumulation = null;
            } else if (++ numReads >= discardAfterReads) {
                numReads = 0;
                // 读取了足够的数据,尝试丢弃一些字节,避免OOM风险
                discardSomeReadBytes();
            }

            int size = out.size();
            decodeWasNull = !out.insertSinceRecycled();
            // 通过管道转发CodecOutputList中的内容
            fireChannelRead(ctx, out, size);
            // 回收数组,清除它并清空内部存储中的所有entry
            out.recycle();
        }
    } else {
        ctx.fireChannelRead(msg);
    }
}

ByteToMessageDecoder.COMPOSITE_CUMULATOR:

public ByteBuf cumulate(ByteBufAllocator alloc, ByteBuf cumulation, ByteBuf in) {
    final ByteBuf buffer;
    // 空间不足或有被引用或只读时,需要扩展(通过替换它)
    if (cumulation.writerIndex() > cumulation.maxCapacity() - in.readableBytes()
            || cumulation.refCnt() > 1 || cumulation.isReadOnly()) {
        /* 扩展缓冲区 */
        buffer = expandCumulation(alloc, cumulation, in.readableBytes());
    } else {
        buffer = cumulation;
    }
    buffer.writeBytes(in); // 写入本次的消息
    in.release(); // 写入后释放
    return buffer;
}

ByteToMessageDecoder:

static ByteBuf expandCumulation(ByteBufAllocator alloc, ByteBuf cumulation, int readable) {
    ByteBuf oldCumulation = cumulation;
    // 增加容量分配新的缓冲区
    cumulation = alloc.buffer(oldCumulation.readableBytes() + readable);
    // 写入旧数据
    cumulation.writeBytes(oldCumulation);
    oldCumulation.release(); // 写入完成后释放旧的缓冲区
    return cumulation;
}

ByteToMessageDecoder:

protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
    try {
        while (in.isReadable()) {
            int outSize = out.size();
            if (outSize > 0) {
                fireChannelRead(ctx, out, outSize);
                out.clear();
                // 如果ChannelHandlerContext已移除,直接退出循环,继续操作缓冲区是不安全的
                if (ctx.isRemoved()) {
                    break;
                }
                outSize = 0;
            }
            int oldInputLength = in.readableBytes();
            /* 解码 */
            decodeRemovalReentryProtection(ctx, in, out);
            // 同样的检查操作
            if (ctx.isRemoved()) {
                break;
            }
            // 判断out长度是否变化
            if (outSize == out.size()) {
                if (oldInputLength == in.readableBytes()) {
                    // 没有消费ByteBuf,说明是个半包消息,需要继续读取后续的数据报,退出循环
                    break;
                } else {
                    // 消费了ByteBuf,继续执行
                    continue;
                }
            }
            // 没有消费ByteBuf,out的长度却变了(解码出了一个或多个对象),这种情况认为是非法的
            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 (Throwable cause) {
        throw new DecoderException(cause);
    }
}

ByteToMessageDecoder:

final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
        throws Exception {
    decodeState = STATE_CALLING_CHILD_DECODE; // 记录解码状态
    try {
        // 子类具体实现解码逻辑
        decode(ctx, in, out);
    } finally {
        boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING;
        decodeState = STATE_INIT;
        if (removePending) {
            handlerRemoved(ctx);
        }
    }
}

到这里ByteToMessageDecoder的源码分析就完成了。

猜你喜欢

转载自blog.csdn.net/heroqiang/article/details/81510059