Pomelo-protobuf
Pomelo-protobuf 协议是一种高效的二进制数据编码协议模块,该模块在 pomelo 中实现 protobuf 协议用于数据传输。当然,Pomelo-protobuf 也可以在其他项目中独立使用。与谷歌 protobuf 不同的是,我们在 pomelo-protobuf 中提供了一个通用的编码器和解码器。我们使用 protos 文件作为元数据来编码/解码消息,因此您不需要向项目添加任何代码,相应的,您需要添加一个protos.json(或两个,用于不同的编码器和解码器消息)文件来定义需要通过 protobuf 编码的消息。pomelo-protobuf 的结构如下:
用法
定义 protos
要使用 pomelo-protobuf,需要编写一个 json 文件来定义消息格式。该文件的语法与 protobuf 中的 .proto 文件相同,但使用 json 格式表示,protos.json 示例如下 :{
"onMove" : {
"required uInt32 entityId" : 1,
"message Path": {
"required uInt32 x" : 1,
"required uInt32 y" : 2
},
"repeated Path path" : 2,
"required uInt32 speed" : 3
},
"onAttack" : {
"required uInt32 attacker" : 1,
"required uInt32 target" : 2,
"message AttackResult" : {
"required uInt32 result" : 1,
"required uInt32 damage" : 2,
"optional uInt32 exp" : 3
},
"required AttackResult result" : 3
}
}
与 google protobuf 不同,我们将所有 proto 都写在同一个文件中,并使用一个唯一的键来定义消息。要使用 protos,我们使用解析器将 protos 文件解析为对机器更友好的格式,即 json 格式,然后您可以使用结果来解码/编码消息。
RootMessage 支持
您可以在 protos 中编写 RootMessage 以供全局使用。{
"message Path": {
"required double x" : 1,
"required double y" : 2
},
"message Equipment" : {
"required uInt32 entityId" : 1,
"required uInt32 kindId" : 2
},
"onMove" : {
"required uInt32 entityId" : 1,
"repeated Path path" : 2,
"required float speed" : 3
},
"area.playerHandler.enterScene" : {
"message Player" : {
"message Bag" : {
"message Item" : {
"required uInt32 id" : 1,
"optional string type" : 2
},
"repeated Item items" : 1
},
"required uInt32 entityId" : 1,
"required uInt32 kindId" : 2,
"required Bag bag" : 3,
"repeated Equipment equipments" : 4
},
"optional Player curPlayer" : 2
}
}
服务端和客户端
Pomelo protobuf 有 js 的服务器代码和客户端代码。服务端代码运行在 Node.JS 环境中,使用缓冲器(Buffer)表示二进制数据。
客户端代码在浏览器上运行,使用字节数组(ByteArray)来表示二进制数据。
平均而言,服务器版本的编码/解码速度比客户端版本快 60%,内存使用量更少。因此,我们强烈建议使用 Node.JS 上的服务器代码以获得更好的性能。
样例消息 protos.json:
var key = 'onMove';
var msg = {
entityId : 14,
path : [{x : 128,y : 796},{x : 677,y : 895}],
speed : 160
};
服务端编/解码
//Require proto buf module
var protobuf = require('protobuf');
//Set encode protos and decode protos
var protos = protobuf.parse(require('./protos.json'));
protobuf.init({encoderProtos:protos, decoderProtos:protos});
//Encode msg to binary Buffer
var buffer = protobuf.encode(key, msg);
//Decode a msg from binary buffer
var decodeMsg = protobuf.decode(key, buffer);
在服务器端,编码结果将是一个Buffer。编码器原型和解码器原型可能不同,在这种情况下,我们对编码器和解码器使用相同的原型。
客户端编/解码
要在浏览器(或其他前端 js 开发工具如 cocos2dx-js)中使用 protbuf,您需要在 html(或js)文件中包含 /client/protbuf.js。//Require proto buf
var protobuf = require('protobuf');
//Get parsed protos from server
var protos = getProtos();
//Init protobuf
protobuf.init({encoderProtos:protos, decoderProtos:protos});
//Encode msg to binary Buffer
var buffer = protobuf.encode(key, msg);
//Decode a msg from binary buffer
var decodeMsg = protobuf.decode(key, buffer);
Protobuf 将是一个全局变量,您需要从服务器获取解析后的 protos。其他的与服务器端相同,只是编码器的结果将由 ByteArray 而不是 Buffer 产生。
兼容性
对于相同的消息和 proto,pomelo protobuf 和 google protobuf 的编码结果是相同的。这意味着你可以与 google protobuf 交换二进制数据。 我们在 proto 文件中进行了一些更改,并且有一些功能我们不支持,不同之处如下:- 打包(package): 默认情况下,会打包包含简单内容(integer、float)的数组,复杂的内容(消息、字符串)没有打包。
- 长整型(long): Pomelo 协议不支持长整型,因为 javascript 中没有长整数。所有大于32位的整数将被转换为64位浮点,该浮点只有52位的有效数字。任何大于52位的整数都将丢失。
- 默认值(default): Pomelo protobuf 不支持默认关键字,因为默认值只用于初始化解码器端的元素,这可以由构造函数完成。
- 枚举(enum): Pomelo-protobuf 不支持枚举关键字。