别再手写 Java 二进制解析了 --- 用 FastProto 从繁琐到优雅
项目地址 :GitHub - indunet/fastproto
还在用 ByteBuffer、位运算、手动偏移量去解析二进制协议吗?
FastProto 想解决的几个典型痛点:
- 协议一长,满屏都是魔法数字和位运算,可读性极差;
- 协议一改(字段新增/调整),到处找偏移量,维护成本极高;
- 出一个线上问题,要对照抓包逐位排查,排错效率很低;
- 新同事接手,只能从
array[offset + 13]这种写法里硬抠含义。
以一条 20 字节的气象报文为例,对应的协议结构大致如下:
| 字节偏移 | 位偏移 | 数据类型(C/C++) | 信号名称 | 单位 | 换算公式 |
|---|---|---|---|---|---|
| 0 | unsigned char | 设备编号 | |||
| 1 | 预留 | ||||
| 2-9 | long | 时间戳 | ms | ||
| 10-11 | unsigned short | 湿度 | %RH | ||
| 12-13 | short | 温度 | ℃ | ||
| 14-17 | unsigned int | 气压 | Pa | p * 0.1 | |
| 18 | 0 | bool | 设备有效标识 | ||
| 18 | 3-7 | 预留 | |||
| 19 | 预留 |
现实中的协议往往比这张表复杂得多:字段更多、版本更多、变更更频繁。
FastProto 的目标,就是让你用 Java 类 + 注解,直接"把这张表写成代码"。
基于这份协议,传统手写解析代码大概是这样:
java
byte[] datagram = ...;
int id = datagram[0] & 0xFF;
long timeMillis =
((long) datagram[2] & 0xFF) |
(((long) datagram[3] & 0xFF) << 8) |
(((long) datagram[4] & 0xFF) << 16) |
(((long) datagram[5] & 0xFF) << 24) |
(((long) datagram[6] & 0xFF) << 32) |
(((long) datagram[7] & 0xFF) << 40) |
(((long) datagram[8] & 0xFF) << 48) |
(((long) datagram[9] & 0xFF) << 56);
Timestamp time = new Timestamp(timeMillis);
int humidity = (datagram[10] & 0xFF) | ((datagram[11] & 0xFF) << 8);
int temperature = (short) ((datagram[12] & 0xFF) | ((datagram[13] & 0xFF) << 8));
boolean deviceValid = (datagram[18] & 0x01) != 0;
字段一多、协议一改,偏移量、字节序、位运算 全靠人脑记,既枯燥又容易犯错。
用 FastProto 写同一份协议,会是什么样?
FastProto 的核心思想:用注解把"协议结构"直接写在 Java 类上------用一个 POJO 把协议"写成代码",做到真正的"代码即协议、协议即代码"。
java
import org.indunet.fastproto.annotation.*;
public class Weather {
@UInt8Type(offset = 0)
int id;
@TimeType(offset = 2)
Timestamp time;
@UInt16Type(offset = 10)
int humidity;
@Int16Type(offset = 12)
int temperature;
@UInt32Type(offset = 14)
long pressure;
@BoolType(byteOffset = 18, bitOffset = 0)
boolean deviceValid;
}
解码和编码只需要两行:
java
byte[] datagram = ...; // 设备发送的二进制报文
Weather weather = FastProto.decode(datagram, Weather.class);
byte[] bytes = FastProto.encode(weather, 20); // 重新封装成 20 字节报文
如果需要做简单换算(比如气压 *0.1),也可以直接写在注解上:
java
public class Weather {
...
@UInt32Type(offset = 14)
@DecodingFormula(lambda = "x -> x * 0.1") // 解码: uint32 -> double
@EncodingFormula(lambda = "x -> (long) (x * 10)") // 编码: double -> uint32
double pressure;
}
这样,协议就变成了一个清晰的 Java 类 :
谁在第几字节、用什么类型、怎么换算,都写在这个 POJO 上,这份类本身就是最权威的协议文档,一眼就能看懂,新同事也能快速接手。
FastProto 帮你省下什么?
-
少写重复代码 :
不再满屏位运算、偏移常量,把注意力放回到"字段定义"和"业务含义"上。
-
协议变更更轻松 :
字段新增/调整时,只需改类和注解,而不是在一堆
offset += 4;里埋头搜索。 -
更容易排查问题 :
二进制抓包一对,看看注解和字段就能快速定位问题,而不是一行一行 print。
FastProto 同时提供非注解 API (链式 decode() / create())、校验和 / CRC 注解 、Netty / Kafka 集成等能力,方便你在不同项目里按需使用。
想试试?可以从这里开始
- 引入依赖(Maven):
xml
<dependency>
<groupId>org.indunet</groupId>
<artifactId>fastproto</artifactId>
<version>3.12.3</version>
</dependency>
- 更完整的使用说明 :可以查看在线文档站点
https://indunet.github.io/fastproto/help.html,
也可以直接阅读项目里的README-zh.md和docs目录。
欢迎交流 & 点个 star 支持一下
FastProto 是一个持续维护的开源项目,很多功能都来自真实业务场景。
非常欢迎你:
- 在实际项目中尝试使用,提 Issue 分享使用体验与改进建议;
- 根据自己的协议场景,提 PR 一起补充文档和示例;
- 把它推荐给也在为二进制解析头疼的 Java 同学。
如果这个项目对你有一点点帮助,
也欢迎在 GitHub 上给一个 star,表示支持。
你的鼓励,会让这个轮子越磨越好。
项目地址 :GitHub - indunet/fastproto 项目提供完善的中文 / 英文 README 以及文档站点支持,从快速上手到进阶用法都有覆盖。