ProtoBuf格式详解

介绍protobuf编码格式。


protobuf是一种数据交换格式,又称PB编码,由Google开源,类似于Json、XML,但其内部是纯二进制格式,比Json,XML等格式要更精炼,主要用于数据的序列化和反序列化,目前官方提供了JAVA、Python、C++等多种语言的实现。


PB格式的解析依赖于消息文件,在其实现中,.proto定义了各个消息项的id值。


直观地,PB编码就是将一个结构体的内容编码成二进制流。例如一段json数据:

 "id":176,

 "age":24,

 "name":"xieyifenxi",

}


.proto文件的定义如下:

message Person { 

required int32 id = 1;

optional int32 age = 2;

required string name = 3;

}

则json数据编码成PB格式则是:

08 b0 01 10 18 1a 0a 78 69 65 79 69 66 65 6E 78 69


通常,在协议解析的过程中碰到的PB编码,是没有.proto文件的,解析的时候,只需要根据数据内容,解析出每一项内容即可,而每一项内容的含义,一般通过分析得到。


在许多APP的数据流中,都会存在protobuf编码。本文将通过对PB编码进行介绍,使大家了解如何在协议分析过程中对其进行解析。


01

数据结构


通过前面的例子,可以看到PB的数据结构就是每项数据独立编码,包含一个表示数据类型wire_type和字段序号field_number的数据头,和对应的数据段内容。即HEAD1+MSG1+HEAD2+MSG2+……


在数据头中,字段的序号field_number在整个数据结构中是唯一的,并且可以乱序、缺失和嵌套,序号是在.proto文件中定义的,协议分析中关心的意义不是很大。


数据类型wire_type则表示数据段内容是什么类型,在protobuf官网上描述了类型的含义:

https://developers.google.com/protocol-buffers/docs/encoding


常见的数据类型wire_type02,分别为Varint类型和Length-delimited变长度数据类型,掌握了这两个类型,基本上在协议解析中,处理PB编码就基本没有障碍了。Varint类型一般就是int数据,而Length-delimited变长度数据类型通常就是字符串数组等数据。


数据头中数据类型和字段序号的组合方式是:(field_number << 3) | wire_type

当然,field_number是Varint类型,需遵循Varint的编码规则。


例如,文首的例子中,id、age、name对应的编码值为:

1 <<< 3 | 0 =0x08

2 <<< 3 | 0 = 0x10

3 <<< 3 | 2 = 0x1a


数据头之后,是数据段,它包含了被编码的数据,不同类型的数据编码格式不同,后面的章节将介绍对应具体类型的编码方法。


02


Varint


Varint是一种对数字进行编码的方法,将数字编码成不定长的二进制数据,数值越小,编码后的字节越少。


编码规则如下:

每个字节的最高位表示下一字节是否仍然是编码的内容,若最高位为1,则下一字节仍然是编码的数字的一部分,若该位为0,则编码到本字节结束。每个字节的后7位,则由小端表示的数字的二进制值,在高位补0凑齐7的倍数位组成。


例如,数值345,其二进制值为 1 0101 1001,在高位补0后分成两个7位 000 0010和 101 1001,则Varint编码结果为:

1 101 1001 0 000 0010

即0xD9 0x02


对文首的例子,由于id 176的二进制值为1011 0000,每七位编码成一个字节,因此,需要用两个字节来表示:

1011 0000 0000 0001

0xB0 0x01


而age 24的二进制值为 1 1000,则只需要一个字节来表示:

0001 1000

即0x18


前面只是弄明白了int32的Varint编码,对协议解析来说,一般已经够用了,除非这个被编码的数,在取出后需要用其特定的含义来进行计算,因为在PB编码中,还考虑了对负数进行Varint编码


当我们按照同样的逻辑对负数进行Varint编码时,会发现,负数编码后占用的字节会很多,这不太合算,因此ZigZag编码在PB中被使用,使得Varint编码可以用较少的位数来对负数进行编码。


PB编码中提供了sint32和sint64类型,使用ZigZag编码,让所有的负数都使用正数表示,计算方式如下:

sint32:

(n << 1) ^ (n >> 31)

sint64:

(n << 1) ^ (n >> 63)


即:

原始值0,通过计算,得到ZigZag表示值为0;

原始值-1,通过计算,得到ZigZag表示值为1;

原始值1,通过计算,得到ZigZag表示值为2;

原始值-2,通过计算,得到ZigZag表示值为3;

原始值2147483647,通过计算,得到ZigZag表示值为4294967294;

原始值-2147483648,通过计算,得到ZigZag表示值为4294967295。

依此类推


在协议还原中,对一个Varint编码的值,想要知道它表达的是int32,还是sint32,就只有想办法找到其对应的.proto文件才可以。


03


Length-delimited


Length-delimited就是对可变长度的数据,在编码时,将长度和数据编码在一起,类似于TLV结构的LV部分,前面为数据长度,后面为由数据长度决定的数据内容,数据长度采用的是Varint编码。


例如文首的例子里,name的值为"xieyifenxi"的长度为10,则编码为:

0a 78 69 65 79 69 66 65 6E 78 69


其中,0x0a为长度值的Varint编码,之后紧接着的是值的内容。


这相当的简单。




对protobuf编码的详解就介绍到这里了,有疑问,可以联系我,或者上其官网了解。它的官网是https://developers.google.com/protocol-buffers/


640?wx_fmt=jpeg

长按进行关注。





Protobuf格式是一种数据序列化和反序列化的方法,常用于不同系统或语言之间的数据交换。与其他数据格式相比,Protobuf格式具有更高的效率和更小的数据大小。 PDF是一种便携式文档格式,用于显示、打印和传输电子文档。它具有保留文档原始格式和布局的特点,用户可以通过各种设备和操作系统查看和处理PDF文件。 将Protobuf格式应用于PDF文件可以带来一些优势。首先,使用Protobuf格式可以节省存储空间,缩小PDF文件的大小。这对于网络传输和存储来说非常重要,能够提高传输速度和减少存储成本。 其次,使用Protobuf格式可以提高数据的序列化和反序列化速度。相比其他XML或JSON等文本格式Protobuf采用二进制编码,使得解析和处理PDF文件更加高效。 另外,Protobuf格式还支持版本控制和扩展性。如果需要对PDF文件进行更新,只需对Protobuf格式进行修改和增加字段,而无需影响旧版本的解析。这为PDF文件的演化和升级提供了灵活性。 当然,将PDF文件转换为Protobuf格式也存在一些限制。首先,Protobuf是面向结构化数据的序列化方法,需要对PDF文件进行结构化处理。这可以通过将PDF文件的内容进行分块和标记来实现。其次,Protobuf需要定义数据的格式和结构,因此需要在应用中进行匹配和解析。 总之,将Protobuf格式应用于PDF文件可以提高数据传输和处理的效率,减小存储空间的占用,并为PDF文件的更新和演化提供了灵活性。但同时,也需要对PDF文件进行结构化处理和进行适配解析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值