Netty 学习(九):解码源码说明

Netty 学习(九):解码源码说明

作者: Grey

原文地址:

博客园:Netty 学习(九):解码源码说明

CSDN:Netty 学习(九):解码源码说明

解码就是不断地从TCP缓冲区中读取数据,每次读取完都需要判断是否为一个完整的数据包。

  1. 如果当前读取的数据不足以拼接成一个完整的业务数据包,那就保留该数据,继续从TCP缓冲区中读取,直到得到一个完整的数据包。

  2. 如果当前读到的数据加上已经读取的数据足够拼接成一个数据包,那就将已经读取的数据拼接上本次读取的数据,构成一个完整的业务数据包传递到业务逻辑,多余的数据仍然保留,以便和下次读到的数据尝试拼接。

使用 Netty 的话,整个过程就变的简单了,不需要用户自己处理粘包的问题。Netty 中定义了一个拆包的基类ByteToMessageDecoder,定义了两个变量

public static final Cumulator MERGE_CUMULATOR = ......
public static final Cumulator COMPOSITE_CUMULATOR = ......

其中MERGE_CUMULATOR的原理是每次都将读取到的数据通过内存拷贝的方式,拼接到一个大的字节容器中,对于不够的情况,还进行了扩容处理

if (required > cumulation.maxWritableBytes() ||
                    required > cumulation.maxFastWritableBytes() && cumulation.refCnt() > 1 ||
                    cumulation.isReadOnly()) {
                        // 扩容!
                    // Expand cumulation (by replacing it) under the following conditions:
                    // - cumulation cannot be resized to accommodate the additional data
                    // - cumulation can be expanded with a reallocation operation to accommodate but the buffer is
                    //   assumed to be shared (e.g. refCnt() > 1) and the reallocation may not be safe.
                    return expandCumulation(alloc, cumulation, in);
                }
                cumulation.writeBytes(in, in.readerIndex(), required);
                in.readerIndex(in.writerIndex());
                return cumulation;

接下来是ByteToMessageDecoder.channelRead()方法是每次从TCP缓冲区读到数据都会调用的方法,主要逻辑包括如下几个

第一步:累加数据。

cumulation = cumulator.cumulate(ctx.alloc(),
                        first ? Unpooled.EMPTY_BUFFER : cumulation, (ByteBuf) msg);

第二步:将累加的数据传递给业务进行拆包。

// 将数据拆分成业务数据包,塞到业务数据容器out中
callDecode(ctx, cumulation, out);

第三步:清理字节容器。

if (cumulation != null && !cumulation.isReadable()) {
                        numReads = 0;
                        try {
                            cumulation.release();
                        } catch (IllegalReferenceCountException e) {
                            //noinspection ThrowFromFinallyBlock
                            throw new IllegalReferenceCountException(
                                    getClass().getSimpleName() + "#decode() might have released its input buffer, " +
                                            "or passed it down the pipeline without a retain() call, " +
                                            "which is not allowed.", e);
                        }
                        cumulation = null;
                    } else if (++numReads >= discardAfterReads) {
                        // We did enough reads already try to discard some bytes, so we not risk to see a OOME.
                        // See https://github.com/netty/netty/issues/4275
                        numReads = 0;
                        discardSomeReadBytes();
                    }

第四步:将业务数据包传递给业务解码器处理。

int size = out.size();
firedChannelRead |= out.insertSinceRecycled();
fireChannelRead(ctx, out, size);

以上就是拆包器基类的主要方法,Netty 封装了一些特定的拆包器,使用起来也比较方便。

包括

行拆包器:LineBasedFrameDecoder

特定分隔符拆包器:DelimiterBasedFrameDecoder

基于长度的拆包器:LengthFieldBasedFrameDecoder

完整代码见:hello-netty

本文所有图例见:processon: Netty学习笔记

更多内容见:Netty专栏

参考资料

跟闪电侠学 Netty:Netty 即时聊天实战与底层原理

深度解析Netty源码

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GreyZeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值