采用“消息头+消息体”定长解包协议,消息头固定4字节含body长度(大端序),解码分两阶段:先收齐4字节头,再按长度收齐完整报文;须显式设ByteBuffer为BIG_ENDIAN,复用buffer避免GC,禁用FixedLengthFrameDecoder而应选LengthFieldBasedFrameDecoder。

在Java基于NIO的自定义RPC框架中,采用“消息头+消息体”的定长解包协议,核心是让接收方能准确识别每个完整报文的边界。这不是简单的字节拼接,而是通过结构化设计解决TCP粘包/拆包问题的关键手段。
消息头必须包含长度字段
消息头需固定长度(如4字节),且其中至少1个字段明确标识后续消息体的字节数。常见做法是将前4字节设为int型长度值(网络字节序),其余字段可扩展版本号、类型标识等。例如:
- 0–3 字节:body length(int,大端序,表示消息体真实字节数)
- 4–7 字节:version + msgType(可选,若需多协议共存)
这样,接收方只要成功读满4字节头,就能立刻知道接下来该收多少字节才算一个完整报文。
解码逻辑需分两阶段处理
NIO Channel读取是异步非阻塞的,一次read()可能只拿到部分数据。因此解码器必须维护状态,不能假定数据一次到位:
立即学习“Java免费学习笔记(深入)”;
- 第一阶段:等待至少4字节——若buffer中不足4字节,暂不解析,继续 accumulate
- 第二阶段:从头4字节提取bodyLength,再判断buffer中是否已有(4 + bodyLength)字节;若不足,继续等待;若满足,则切出完整报文,剩余数据留待下一轮处理
注意字节序与内存管理细节
Java NIO默认使用本机字节序,而网络传输要求统一为大端(Big-Endian)。务必用ByteBuffer.order(ByteOrder.BIG_ENDIAN)显式设置;否则跨平台通信会错读长度。同时避免反复创建ByteBuffer:
- 读取头时用getInt(),不要用array()转byte[]再手动解析
- 读取body时用get(byte[] dst, offset, length),避免flip/compact误操作
- 整个过程建议复用同一个heap或direct buffer,减少GC压力
不推荐纯FixedLengthFrameDecoder硬编码
Netty的FixedLengthFrameDecoder适用于每条消息严格等长的场景(如日志行、传感器采样点),但RPC请求/响应长度天然不固定。若强行设为最大可能长度(如4KB),会导致小请求严重浪费带宽和内存。真正可行的是LengthFieldBasedFrameDecoder,并配置:
- lengthFieldOffset = 0(长度字段从头开始)
- lengthFieldLength = 4(占4字节)
- lengthAdjustment = 0(长度仅含body,不含header本身)
- initialBytesToStrip = 0(保留header供后续解码器解析)

















