Netty 学习(九):解码源码说明
Netty 学习(九):解码源码说明
作者: Grey
原文地址:
解码就是不断地从TCP缓冲区中读取数据,每次读取完都需要判断是否为一个完整的数据包。
-
如果当前读取的数据不足以拼接成一个完整的业务数据包,那就保留该数据,继续从TCP缓冲区中读取,直到得到一个完整的数据包。
-
如果当前读到的数据加上已经读取的数据足够拼接成一个数据包,那就将已经读取的数据拼接上本次读取的数据,构成一个完整的业务数据包传递到业务逻辑,多余的数据仍然保留,以便和下次读到的数据尝试拼接。
使用 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专栏
参考资料#
作者:GreyZeng
出处:https://www.cnblogs.com/greyzeng/p/16767141.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
你可以在这里自定义其他内容
本文来自博客园,作者:Grey Zeng,转载请注明原文链接:https://www.cnblogs.com/greyzeng/p/16767141.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2021-10-07 Java 中的引用类型和使用场景