ProtoBuf编码原理

article/2025/8/30 8:07:00

背景

Protobuf是我们在网络传输中经常会用到的协议,优点是版本间兼容性强,对数据序列化时的极致压缩使得Protobuf包体积比xml、json等格式要小很多,节约流量。对于pb协议的具体使用方法,其官网有比较详细的说明,本文不再详述。我们的数据不管在代码中是什么复杂结构体,传输时都要序列化成二进制串。官网中也介绍了Protobuf的序列化算法,不过给的例子比较简单,学习起来不够直观。因此,本文用一个较为完整的例子直观展示一下Protobuf的序列化,一个例子即可搞懂Protobuf的序列化算法。

一个完整的Protobuf举例

在这里插入图片描述
一个完整的protobuf例子,包含全部pb支持的数据类型

这个例子有一个特点,就是AllDataType这一结构体中包含了Protobuf所支持的全部数据类型。下一步,使用protoc编译该proto文件,并在程序中声明一个AllDataType类型的数据,将其序列化,并打印出来。下面的代码是以golang为例:

package main
import ("fmt""<path-to>/example""github.com/golang/protobuf/proto"
)
func main() {test := example.AllDataType{Fint32:      257,Fint64:      -2,Fuint32:     1,Fuint64:     1025,Fsint32:     0,Fsint64:     -2,Ffixed32:    17,Ffixed64:    2049,Fdouble:     -0.1,Ffloat:      0.6,Fbool:       true,Fenum:       example.DayOfWeek_SUNDAY,Fmessage:    &example.Child{Fsint64: 3},Fmap:        map[uint32]float64{3: -0.1, 0: 2.12},Frepeatbool: []bool{true, false, true},Fstring:     "Hello World",Fbytes:      []byte{129, 0, 19, 56},Fsfixed32:   12345,Fsfixed64:   54321,}data, _ := proto.Marshal(&test) // protobuf将结构体序列化为二进制串fmt.Println(data) // 打印AllDataType类型的数据序列化后的二进制串
}

最后一行打印的结果为:

在这里插入图片描述
测试程序的输出结果

序列化结果分析

接下来就是最关键的一幅图,我们逐个字节地来分析一下上面的打印结果中,每个字节所代表的含义(可查看大图):

在这里插入图片描述
逐字节解释序列化结果的含义

【1】图中橙色部分(如第1行第1列,第1行第4列)用于表示字段field number(简写为fn)以及wire type(简写为wt)。其中field number是proto文件中标注的该字段数字代号,而wire type表示本字段的数据类型属于哪种归类,这些归类主要用于提醒反序列化程序如何判断本字段值占据几个字节。Wire type值与数据类型的映射关系为:

Wire Type解释数据类型
0varint变长整型(见下文)int32, int64, uint32, uint64, sint32, sint64, bool, enum
1固定8字节fixed64, sfixed64, double
2需显式告知长度(见下文)string, bytes, 嵌套类型(embedded messages),repeated字段
3(已废弃)(已废弃)
4(已废弃)(已废弃)
5固定4字节fixed32, sfixed32, float

因为wire type种类很少,为了进一步节约字节,write type只用了3个bits来表示,而fn则使用更高位来表示:

在这里插入图片描述
【2】参考【1】中的表格,大部分整数类型的wire type都是varint变长整型。Varint简单说就是每个字节最高位不用来表示具体数值,只用来表示“本字节是不是这个数字的最后一个字节”。0表示最后一个字节,1表示不是最后一字节、后面还有。因此如果要把varint还原为普通的二进制表示,需要去掉最高bit,把剩下的7个bit组合起来看。一图胜千言:

在这里插入图片描述

需要注意protobuf的varint采用类似小端模式,因此图中第1行第3列存的是高位,第2列是低位,转化十进制过程中需要把他们调换一下位置,其他使用varint的类型也是类似机制。

【3】注意从第1行第5列到第2行第1列,所存储的数字是int64类型的-2,占据10个字节,这甚至比不使用varint所占的空间还要大。主要原因是负数在计算机中采用补码存储,int类型-2本来存储上就等同于一个特别大的unsigned int,需要用很多字节去存储,而varint还把每字节8bits最高位用来干别的事情,不用来表示数值了,导致每个字节都少了一个可用的bit位。这里也可以看出只使用varint的弊端,对于int类型的负数(或unsigned int类型的特别大正数),完全没有起到节约字节数的效果。因此protobuf中出现了sint32和sint64类型,该类型使用ZigZag来优化。ZigZag规则为,如果是负数,则存储其绝对值的2倍减1;如果为非负数,则存储其绝对值的2倍。这样就可以把int类型1对1映射为unsigned int类型。这一规则对应图中的第2行第8列,数字-2其实二进制存储的是正整数3([zz]表示ZigZag)。

ZigZag的优化主要基于一个事实:我们在传输数据时,所传的整数大多是和0比较接近的小正数或者小负数,很少传输绝对值特别大的负数或正数。满足这一事实的场景下,推荐把protobuf中的int32和int64都替换为sint32和sint64,节约字节数。

【4】Varint和ZigZag方法其实没有优化绝对值特别大的数。例如如果要传输的数字是int32最大值2147483647,本来是4个字节,使用varint反而需要5个字节了。因此,fixed32和fixed64类型就是为这种场景设计。这种情况下,数字直接按照它的二进制表示进行序列化,固定占用4字节或8字节,例如图中的第3行第2列到第9列,表示的是2049。由于2049是一个比较小的整数,因此会有很多0来填充空余字节,比较浪费。

【5】Protobuf表示浮点数的类型是double(8字节)和float(4字节)。浮点数也是直接按照它的二进制表示进行序列化。例如第4行第7列至第10列,4字节浮点数0.6被序列化为 [154 153 25 63] (小端模式ASCII码),这正是0.6在内存中的存储方式。至于怎么算出来的,不再详细展开,可以参考链接。

【6】Protobuf序列化时会直接忽略为空值的字段,例如fn=5的字段根本没有在图中出现,主要原因是fn=5的字段Fsint值为0,属于空值(默认值)。直接忽略可进一步节约字节数。

【7】图中的浅紫色字段表示的是字段长度,它专属于wire type=2的字段。当wire type=2时,protobuf并不知道对应的值到底占据几个字节,需要在fn和wt后面紧跟一个长度数字。需要注意的是字段长度数值也属于varint表示的无符号整型。

【8】对于在proto文件中用repeat修饰的字段,值部分会连续出现多次,如第7行第6列到第10列。一般repeat字段都被当成数组。

【9】注意第8行第12列和第13列,当fn大于15时,一个字节已经不足以序列化fn+wt部分了,这时fn+wt会被当成一个数字,按照varint的形式来序列化。

在这里插入图片描述
field number大于16时,必须使用多个字节表示

因为fn大于15的字段需要至少2个字节来存储fn+wt,protobuf建议,把fn小于16的值留给最常用字段,以节约字节数

最后,总结一下在Protobuf协议定义时,怎样选取合适的整数类型:

(1)有符号整型,大多数值都不算很大(4字节绝对值小于227,8字节绝对值小于255),使用sint32和sint64

(2)有符号整型,大多数值都特别大(4字节绝对值大于227,8字节绝对值大于255),使用sfixed32,sfixed64

(3)无符号整型,大多数值都不算很大(4字节绝对值小于228,8字节绝对值小于256),使用uint32和uint64

(4)无符号整型,大多数值都特别大(4字节绝对值大于228,8字节绝对值大于256),使用fixed32和fixed64

小于228,8字节绝对值小于256),使用uint32和uint64

(4)无符号整型,大多数值都特别大(4字节绝对值大于228,8字节绝对值大于256),使用fixed32和fixed64

(5)有符号整型,绝大多数数值都是不算很大的正数(4字节绝对值小于227,8字节绝对值小于255),但在极少数情况下可能出现负值,使用int32和int64。


http://chatgpt.dhexx.cn/article/dQfGeXBo.shtml

相关文章

windows protobuf编译

protobuf编译 Protobuf下载地址&#xff1a;https://github.com/protocolbuffers/protobuf/releases 1、配置cmake: 2、点击生成&#xff0c;打开工程文件&#xff1a;略 3、编译protobuf: 4、安装完成展示&#xff1a; 测试&#xff1a; 1、创建在bin目录下创建build.bat…

【Protobuf】Protobuf协议

Protobuf协议 什么是Protobuf一、编写proto文件二、生成协议类三、编码解码3.1 编码方法3.2 解码方法 什么是Protobuf Protobuf是谷歌发布的一套协议格式&#xff0c;它规定了一系列编码和解码方法。 目前&#xff0c;网上已经有不少实现Protobuf编码解码的库&#xff0c;可以…

protobuf简介

文章目录 一、protobuf的定义二、protobuf的优缺点2.1、优点2.2、缺点 三、protobuf的使用流程3.1、protobuf在Linux下的安装过程3.2、定义proto文件3.3、protoc编译器3.4、调用接口进行序列化、反序列化 四、protobuf的应用场景五、protobuf与json和XML的对比 一、protobuf的定…

java中使用protobuf总结

基本没怎么接触过java编程&#xff0c;别的团队发过来一个用java编写的存储pb的文件&#xff0c;让拆分和解析&#xff0c;硬着头皮做一下&#xff0c;在此将步骤做个记录&#xff1a; 下载安装protobuf https://github.com/protocolbuffers/protobuf/tags?afterv3.6.1.2 编译…

protobuf 详解

protobuf简介 Protobuf是Protocol Buffers的简称&#xff0c;它是Google公司开发的一种数据描述语言&#xff0c;是一种轻便高效的结构化数据存储格式&#xff0c;可以用于结构化数据串行化&#xff0c;或者说序列化 。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议…

protobuf介绍和语法

目录 前言 语法 标识符 字段 字段类型 proto2和proto3区别 前言 Protobuf即Protocol Buffers&#xff0c;是Google公司开发的一种跨语言和平台的序列化数据结构的方式&#xff0c;是一个灵活的、高效的用于序列化数据的协议。 与XML和JSON格式相比&#xff0c;pr…

Protobuf:一种更小、更快、更高效的协议

C/CLinux服务器开发/后台架构师知识体系 Protobuf介绍 Protobuf (Protocol Buffers) 是谷歌开发的一款无关平台&#xff0c;无关语言&#xff0c;可扩展&#xff0c;轻量级高效的序列化结构的数据格式&#xff0c;用于将自定义数据结构序列化成字节流&#xff0c;和将字节流反…

win10商店打不开_win10应用商店闪退是咋回事呢

win10虽然具有闪电般的开机速度&#xff0c;并且还新增了很多功能。但比较是全新的操作系统&#xff0c;所以难免会存在一些故障&#xff0c;这里小编就给大家讲讲win10应用商店闪退打不开怎么解决。 方法一 1&#xff0c;首先&#xff0c;打开开始菜单&#xff0c;进入设置&am…

电脑安装Linux闪退,win10系统运行内置Linux系统闪退如何处理

我们在win10系统电脑的使用中&#xff0c;有小伙伴在Linux系统的使用中出现了问题&#xff0c; win10系统运行内置Linux系统闪退的情况出现了&#xff0c;这是什么原因导致的呢&#xff0c;我们在win10系统运行内置Linux系统闪退如何处理&#xff0c;今天小编就来跟大家分享一下…

Java版mc闪退_本文传授win10运行mc闪退的具体操作对策

我们在使用电脑的时候遇到了win10运行mc闪退问题确实比较难受&#xff0c;要是你的电脑技术没有达到一定的水平&#xff0c;可能就不能解决这个win10运行mc闪退的情况。我们应当如何处理这个问题呢&#xff1f;小编先给大伙说说简单的措施&#xff1a;1、确保电脑中安装了 .NET…

(2022.5.27)【Win10】Windows10重置后微软商店闪退打不开、图片闪退打不开、UWP应用闪退打不开——可能的解决方案

更新日志 20220609 增加注意事项 注意事项 经过多为网友的反馈&#xff0c;目前这个方法是无法直接解决微软商店打不开的问题。因此&#xff0c;基于我目前的了解&#xff08;6月9日&#xff09;&#xff0c;如果大家遇到这个问题&#xff0c;真的只能重新 U 盘安装系统了。…

win10内置计算机和天气闪退,win10系统中天气闪退怎么办?Win10天气应用闪退问题解决方法...

win10系统中天气闪退怎么办&#xff1f;最近有部分用户在安装了win10系统后发现自带的天气应用出现闪退的情况&#xff0c;点击天气应用&#xff0c;发现它启动了很久&#xff0c;然后就自动关闭了。之后再点击天气应用就闪退&#xff0c;打不开。而尝试打开别的应用却可以正常…

解决WIN10下应用商店不能用,闪退的情况

解决WIN10下应用商店不能用,闪退的情况 先说下我的情况,也是博主手贱,经常看PC上的某个文件或者程序不顺眼的话就会想办法把它干掉,为此重装过几次系统… 这一次是装了win10的周年更新后,烦人的cortana,onedrive等一些我不想要的APP又回来了,在暴力清理这些APP的时候,需要特殊…

win10java闪退怎么办_Win10应用打不开或闪退怎么办?解决方案在此

可能有一些用户升级Win10之后遇到了应用商店、应用打不开或闪退的问题&#xff0c;此时可尝试通过下面的一些方法来解决。 1、点击任务栏的搜索(Cortana小娜)图标&#xff0c;输入Powershell&#xff0c;在搜索结果中右键单击Powershell&#xff0c;选择“以管理员身份运行”。…

win10的c语言程序闪退,Win10专业版软件打不开闪退怎么办?

现在用到最多的Win10系统是Win10专业版&#xff0c;用户重装Win10专业版系统的目的就是为了解决电脑遇到的问题&#xff0c;然而重装系统后还是会出现许许多多的问题&#xff0c;比如说部分软件打不开了&#xff0c;闪退的问题。如果您也遇到了相同的问题&#xff0c;下面就是小…

win10安装虚拟机闪退_win10应用商店战争机器4闪退,无法运行。

创建日期 2018/01/07 win10应用商店战争机器4闪退&#xff0c;无法运行。 日志名称: System 来源: Microsoft-Windows-DistributedCOM 日期: 2018-01-07 14:05:10 事件 ID: 10001 任务类别: 无 级别: 错误 关键字:…

右击计算机管理打开会闪退,win10应用商店为什么会闪退 win10应用商店出故障怎么修复...

win10系统有个应用商店&#xff0c;在商店里用户可以下载一些软件应用&#xff0c;很多用户反馈win10应用商店老是闪退&#xff0c;重启也没有用&#xff0c;这该怎么办&#xff1f;下面小编为大家科普下win10应用商店闪退的解决方案&#xff0c;希望可以帮助到大家。 win10应用…

win10的c语言程序闪退,win10内置应用出现闪退怎么回事? win10打开应用总闪退的解决方法...

Windows10操作系统新增加很多实用的功能&#xff0c;对大家操作电脑有很大帮助。Win10专业版系统自带有相机功能、地图功能、时钟功能&#xff0c;同时还有一个应用商店功能&#xff0c;有的小伙伴说打开内置应用时出现闪退&#xff0c;究竟是哪里出现问题&#xff1f;针对此问…

win10的c语言程序闪退,win10 1909系统出现应用闪退如何解决

许多用户在升级更新到win10 1909版本系统之后&#xff0c;反映说遇到这样一个问题&#xff0c;就是使用应用的时候会出现闪退的现象&#xff0c;该怎么处理呢&#xff0c;下面给大家带来win10 1909系统出现应用闪退的解决措施。 一、重装应用 将闪退的应用卸载之后重新安装一下…

Window10 应用商店闪退问题

新装win10后打开应用商店&#xff0c;搜索软件直接闪退&#xff0c;查看了网上前人留下的经验发现还是没有解决问题&#xff0c;后来点开了电脑的设置 默认是选中第二项&#xff0c;点击第一项后&#xff0c;应用商店可以使用了 当然选中开发人员模式应用商店也是可以使用的