ProtoBuf.js 使用技巧

【转】http://www.maxzhang.com/2015/09/ProtoBuf-js%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/

Protocol Buffers

Protocol buffers 是一个用来序列化结构化数据的技术,支持多种语言诸如 C++、Java 以及 Python 语言,可以使用该技术来持久化数据或者序列化成网络传输的数据。相比较一些其他的 XML 技术而言,该技术的一个明显特点就是更加节省空间(以二进制流存储)、速度更快以及更加灵活。

具体参见 Google 开发文档:https://developers.google.com/protocol-buffers/docs/overview

ProtoBuf.js

上面抄的内容不是本文重点,重点是 Google 没有推出官方的 JavaScript 库,在神奇的同性交友社区 Github 上,我找到了一个纯绿色无公害的 ProtoBuf.js 库。它是一个由纯 JavaScript 实现构建在 ByteBuffer.js 之上的 .proto 文件解析库,包含 Message 构建、数据序列化和反序列化等功能。

Google Protocol Buffers 传输的数据是二进制格式,JavaScript 天生不具备处理二进制数据的能力,所以要依赖 ByteBuffer.js ,ByteBuffer 和 ProtoBuf 都是由同一个团队 dcode.io 出品,ByteBuffer 可以单独使用,兼容 IE8+。

.proto 文件

.proto 文件是 Protocol Buffers 的结构化数据定义,结构化数据被称为 Message,具体的就不解释了,可以看最末的两篇参考文章。

ProtoBuf.js 可以解析 .proto,构建 Message 对象,实现数据的序列化和反序列化,对于 .proto 不了解可以看官方文档:https://developers.google.com/protocol-buffers/docs/proto3

下面举几个例子简单说明:

.proto文件初始化和构建 Message,例子参见:https://github.com/dcodeIO/ProtoBuf.js/blob/master/examples/websocket/www/index.html

1
2
var builder = ProtoBuf.loadProtoFile("./example.proto");
var Message = builder.build("Message");

对于声明了 package 的.proto,只需在构建时把包名带上就行。

1
2
var builder = ProtoBuf.loadProtoFile("./example.proto");
var Message = builder.build("com.xxx.Message");

使用loadProtoFile()会让.proto文件明文暴露,所以可以使用 ProtoBuf.js 提供的工具将.proto转义成 json 或 js,参见:https://github.com/dcodeIO/ProtoBuf.js/wiki/pbjs

安装 node 模块:

1
npm install -g protobufjs

以 example.proto 为例,在终端执行:

1
pbjs src/address_book.proto -t js

会输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var _root = dcodeIO.ProtoBuf.newBuilder({})['import']({
    "package": null,
    "messages": [
        {
            "name": "Message",
            "fields": [
                {
                    "rule": "required",
                    "type": "string",
                    "name": "text",
                    "id": 1
                }
            ]
        }
    ]
}).build();

在实际应用时,通常一个 .proto 文件里面会有很多个 Message 类型,所以会将输出结果保存为一个 builder,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var builder = dcodeIO.ProtoBuf.newBuilder({})['import']({
    "package": null,
    "messages": [
        {
            "name": "Message",
            "fields": [
                {
                    "rule": "required",
                    "type": "string",
                    "name": "text",
                    "id": 1
                }
            ]
        }
    ]
});

存成 builder 就可以根据需求构建 Message 类型对象,

1
2
3
4
var Message = builder.build("Message");
var msg = new Message({
  text: 'message from maxzhang.'
});

序列化和反序列化

在 Web 端使用 Protocol Buffers 时,无论发送还是接收的数据都应当是二进制格式,二进制数据可以直接使用 Message 类型对象解析,

1
2
3
4
5
6
7
8
9
10
11
12
// 序列化
function encode(jsonData) {
  var Message = builder.build("Message");
  var msg = new Message(jsonData);
  return msg.toArrayBuffer();
}

// 反序列化
function decodeMessage(data) {
  var msg = builder.build("Message").decode(data);
  return msg;
}

decode() 返回一个 Message 实例对象(可以等同于 JSON Object),实例中的属性便是 .proto 文件中声明的变量与类型,

在 .proto 文件中声明数据类型需要遵循 Protocol Buffers 数据类型 规则,如下表:
Protocol Buffers 数据类型

由于上图不包括 JavaScript 对应的数据类型,所以我自己补充了一个数据类型对应关系(每种数据类型我并没有一一验证使用过,可能有误,欢迎指正):

.proto TypeJavaScript Type
doubleLong
floatfloat
int32int
int64Long
uint32int
uint64Long
sint32int
sint64Long
fixed32int
fixed64Long
sfixed32int
sfixed64Long
boolboolean
stringstring
bytesByteBuffer

ByteBuffer.js

bytes 类型是二进制格式数据,需要使用 ByteBuffer.js 处理,ByteBuffer 可以直接操作二进制数据,例子:

1
2
3
4
5
6
var ByteBuffer = require("bytebuffer");

var bb = new ByteBuffer()
            .writeIString("Hello world!")
            .flip();
console.log(bb.readIString() + " from ByteBuffer.js");

ByteBuffer 可以直接写入或读取任意一种类型的值,值得长度为 8bits - 64bits,特殊的按位写入需要使用 JavaScript 位移操作符,比如:

1
2
3
4
5
6
// 写入数据格式 len of id(4bits) + id(12bits)
var bb = new ByteBuffer(16);
var id = 1;
bb.writeInt8(String(id).length << 4);
bb.writeInt8(id);
bb.flip();

更多 ByteBuffer 接口参见API:https://github.com/dcodeIO/ByteBuffer.js/wiki/API

在 Message API 中的 toArrayBuffer() toBuffer() 等方法底层实际调用的是 ByteBuffer 的接口,与 ByteBuffer 不同的是“Message 对象是按照 JSON 的方式修改值,调用 toArrayBuffer() 接口序列化数据,调用 decode() 接口反序列数据”。

Long.js

由于 JavaScript 精度问题,所以 int64 和 uint64 等类型数据会被转换成 Long.js 对象实例,Long 并不太复杂,与 bignumber.js 类似,具体参考 Long.js API.

WebSocket

关于 WebSocket 提供一个简单的例子
实际应用与例子差不多,就是做两件基础的事:

  • 连接 WebSocket,从 socket 通道拿到二进制数据,反序列化解析成 Message 对象。
  • 实例化 Message 对象,然后序列化成二进制数据,发送给服务端。

参考文章

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值