Java 的 Protobuf 序列化

Java 的 Protobuf 序列化
MeteorCat之前 传奇类MMORPG 游戏都会去裸写二进制数据来交换, 但是需要客户端去定制处理包装二进制数据.
这种一般和客户端交换数据比较麻烦, 比如定义格式:
1 | 请求协议二进制: |
后续为了出于开发效率和性能大部分采用通用 Google Protobuf 来做数据协议交换:
因为其广泛跨语言和跨平台特性在游戏应用也很广, 这里主要是选择 Java 版本来引入和生成.
目前的 Protobuf 有以下版本:
-
proto 2: 很早期的版本, 基本上处于维护, 官方不推荐采用 -
proto 3: 常规维护版本, 追加比较新特性, 移除部分特性实现 -
proto editions: 热更新版本, 特性语法变动很频繁, 不推荐使用
日常使用推荐采用
Proto 3版本, 后续 Java 采用maven来做项目管理
这里的 POM 文件配置如下:
1 |
|
上面就是基础的依赖文件, 后续如果想扩充功能可以在上面代码之中追加, 然后就是创建项目之中的 proto 目录:
1 | 以下命令都是基于 Linux 的目录文件创建功能, 其他平台可以直接手动创建即可 |
user.proto 文件内容如下:
1 | // 声明采用 proto v3 版本处理 |
这里可以采用 IDEA 或者 maven 去触发 mvn compile 编译 Protobuf 的任务, 一般成功打印如下:
1 | [INFO] Compiling 1 proto file(s) to /data/pico/target/generated-sources/protobuf/java |
这里的 /data/pico 就是我的项目目录, 执行完成就在 target/generated-sources/protobuf/java 生成绑定文件.
注意: 编译完成如果发现 IDEA 没办法扫描到代码,
可以将 target/generated-sources/protobuf/java 作为 Sources(源代码) 加入扫描目录.
在 IDEA 目录菜单找到
target/generated-sources/protobuf/java目录右键菜单Mark Directory as选择Sources
之后编写个测试类确认生成:
1 | package io.meteorcat.game; |
最终输出内容:
1 | Enter Request Server ID: 10 |
注意这里有个最大的问题, 那就是 Protobuf 消息只对序列化/反序列化数据负责, 而不对数据正确性负责!
以上面说的字节流数据做例子:
1 | # 请求响应字节流每个 byte 位数据 |
Protobuf 消息解析以 弱类型兼容性优先:
-
只校验二进制流的 “语法合法性”(如字段编号、数据类型的基础格式)
-
不校验 “语义匹配性”(如是否属于目标消息类型)
-
内部字段解析采用 “有数据则填充, 无数据则忽略取默认”
而 (int16 + int16) 和 (int32) 对于 Protobuf 本身来说都是 int32 没有附加细节说怎么组成, 对程序来说就是 “数据”!
为什么要这么设计? 主要为了保障以下状况而兼容:
-
当服务端升级消息并新增字段时, 老客户端解析新字节流, 会忽略新增字段从而不会崩溃
-
当老服务端解析新客户端的字节流, 也会忽略不认识的字段从而正常运行



