Protobuf协议格式详解

protobuf 是google开源的一个序列化框架,类似xml,json,最大的特点是基于二进制,比传统的XML表示同样一段内容要短小得多。还可以定义一些可选字段,用于服务端与客户端通信。前面几篇文章说了protobuf的用法,看到网上也没有分析protobuf协议的文章,就利用一些时间写了 protobuf 的协议分析,希望大家喜欢。

protobuf协议核心思想

基于128bits的数值存储方式(Base 128 Varints)

数据表示方式:每块数据由接连的若干个字节表示(小的数据用1个字节就可以表示),每个字节最高位标识本块数据是否结束(1:未结束,0:结束),低7位表示数据内容。(可以看出数据封包后体积至少增大14.2%)

数字1的表示方法为:0000 0001,这个容易理解

数字300的表示方法为:1010 1100 0000 0010

protobuf字节序是小端字节序,所以这个数字实际是0000 0010 1010 1100

1010 1100 0000 0010
→ 010 1100  000 0010

如下:
000 0010  010 1100
→  000 0010 ++ 010 1100
→  10 0101100
→  256 + 32 + 8 + 4 = 300


基于序号的协议字段映射(类似key-value结构)

所以字段可以乱序,可缺段(记optional)

message person{
    required string name      = 1;
    required string country  = 2;
    optional int32 age           = 3;
}

效果相当于json数据:person = [{1: "john"}, {2:  "USA"}, {3: 30}],其中{3: 30} 还可以不传,person还可以传成 [{2:  "USA"}, {1: "john"}],对端仍旧可以正常解析。


基于无符号数的带符号数表示(ZigZag 编码)

原始的带符号数ZigZag编码后的表示
00
-11
12
-23
21474836474294967294
-21474836484294967295
使用 zigzag 编码,充分利用基于128bits的数值存储(Base 128 Varints)的 技术,只需要加多1个位来表示符号。当绝对值小的数字非常有利,这种方式可以有效减少协议内容长度。

sint32类型编码如下:
(n << 1) ^ (n >> 31)

sint64类型编码如下:
(n << 1) ^ (n >> 63) 

协议数据结构

protobuf怎么在一长串二进制中表示若干个数据

做法就是每块数据前加一个数据头,表示数据类型及协议字段序号。

msg1_head + msg1 + msg2_head + msg2 + ...

数据头也是基于128bits的数值存储方式,一般1个字节就可以表示:

message Test1 {
    required int32 a = 1;
}

如上创建了 Test1 的结构并且把 a 设为 2,序列化好的二进制数据为:
0 000 1000  0 000 0010

以上数据转成十六进制也就是 08 02,其中 8 是怎么得到的?

000 1 000
低3位表示数据类型:0,其他表示协议字段序号:1,加上最高位0, 结果就是8

数据类型的表示如下:

类型含义用于哪些数据类型
0Varintint32, int64, uint32, uint64, sint32, sint64, bool, enum
164-bitfixed64, sfixed64, double
2Length-delimitedstring, bytes, embedded messages, packed repeated fields
3Start groupgroups (deprecated)
4End groupgroups (deprecated)
532-bitfixed32, sfixed32, float


写在最后

protobuf的优缺点

优点前面也提到了,主要有两个:

1、序列化和反序列化效率比 xml 和 json 都高(这个protobuf 自己做了测试,链接要翻墙);

2、字段可以乱序,欠缺,因此可以用来兼容旧的协议,或者是减少协议数据。


但是字段允许乱序欠缺,反过来也是缺点。所以这里总结 protobuf 两个缺点,一个跟这有关:

1、如果字段过多,或者嵌套过深,都会影响反序列化效率,解析每一块数据都要根据序号找到对应的位置然后再插入到已解析好的数据中。

2、数据基于128bits的存储方式,单块数据比较大时效率很受影响。解析数据需要取到所有字节的低7位,然后再拼成一整块数据。

以上两个缺点,特别是对于erlang这类没有指针的语言来说,代价就相当昂贵。

3、协议序号也要占空间,序号越大占空间越大,当序号小于16时无需额外增加字节就可以表示。

protobuf 分析题

最后贴一道分析题,如果看得懂,基本就了解 protobuf 的协议内容了

message Test2 {
    required int32 a = 3;
}
创建了Test2结构,a 赋值 150, 结果是
0001 1000 1001 0110  0000 0001
这个数写成十进制就是 24 150 1,怎么得到这个数据?

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Protocol Buffers,简称protobuf,是一种轻便高效的数据交换格式,类似于XML和JSON,但是protobuf更加小巧、快速、简单,同时也支持多种编程语言。protobuf主要包括三个部分:语言无关的格式定义语言、编译器和库。 protobuf格式定义语言类似于XML Schema和JSON Schema,用于定义数据结构和消息格式protobuf使用类似于IDL的语法来定义数据结构,可以定义消息类型、字段等,同时还支持嵌套和继承等特性。 protobuf的编译器可以将格式定义语言编译成多种编程语言的代码,包括C++、Java、Python、Go等等,这样我们就可以在不同的编程语言之间进行数据交换,而不必担心数据格式的兼容性问题。 protobuf的库提供了序列化和反序列化消息的方法,同时也提供了其他一些辅助方法,例如解析消息、验证消息等。 protobuf的优点包括: 1. 轻便高效:protobuf使用二进制编码,相比于XML和JSON,它更加小巧、快速、简单。 2. 多语言支持:protobuf支持多种编程语言,可以方便地进行跨语言数据交换。 3. 可扩展性:protobuf支持消息的嵌套和继承,可以方便地进行数据结构的扩展和修改。 4. 兼容性:protobuf支持数据格式的向前和向后兼容,可以方便地进行升级和降级。 5. 安全性:protobuf支持消息的加密和签名,可以保证数据的安全性。 总之,protobuf是一种优秀的数据交换格式,可以帮助我们解决跨语言数据交换的问题,同时还具有轻便高效、多语言支持、可扩展性、兼容性和安全性等优点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值