Protubuf入门

news2024/9/27 9:23:35

⼀、初识 ProtoBuf

1. 序列化概念

序列化和反序列化

序列化:把对象转换为字节序列的过程 称为对象的序列化。
反序列化:把字节序列恢复为对象的过程 称为对象的反序列化。

什么情况下需要序列化

存储数据:当你想把的内存中的对象状态保存到⼀个⽂件中或者存到数据库中时。
⽹络传输:⽹络直接传输数据,但是⽆法直接传输对象,所以要在传输前序列化,传输完成后反
序列化成对象。例如我们之前学习过 socket 编程中发送与接收数据。

如何实现序列化

xml、json、 protobuf

2. ProtoBuf 是什么

Protocol Buffers 是 Google 的⼀种语⾔⽆关、平台⽆关、可扩展的序列化结构数据的⽅法,它可⽤ 于(数据)通信协议、数据存储等。
Protocol Buffers 类⽐于 XML,是⼀种灵活,⾼效,⾃动化机制的结构数据序列化⽅法,但是⽐
XML 更⼩、更快、更为简单。
你可以定义数据的结构,然后使⽤特殊⽣成的源代码轻松的在各种数据流中使⽤各种语⾔进⾏编写和 读取结构数据。你甚⾄可以更新数据结构,⽽不破坏由旧数据结构编译的已部署程序。
简单来讲, ProtoBuf(全称为 Protocol Buffer)是让结构数据序列化的⽅法,其具有以下特点:
语⾔⽆关、平台⽆关:即 ProtoBuf ⽀持 Java、C++、Python 等多种语⾔,⽀持多个平台。
⾼效:即⽐ XML 更⼩、更快、更为简单。
扩展性、兼容性好:你可以更新数据结构,⽽不影响和破坏原有的旧程序

3. ProtoBuf 的使⽤特点

1. 编写 .proto ⽂件,⽬的是为了定义结构对象(message)及属性内容。
2. 使⽤ protoc 编译器编译 .proto ⽂件,⽣成⼀系列接⼝代码,存放在新⽣成头⽂件和源⽂件中。
3. 依赖⽣成的接⼝,将编译⽣成的头⽂件包含进我们的代码中,实现对 .proto ⽂件中定义的字段进⾏ 设置和获取,和对 message 对象进⾏序列化和反序列化。

ProtoBuf 是需要依赖通过编译⽣成的头⽂件和源⽂件来使⽤的。有了这种代码⽣成机制,
开发⼈员再也不⽤吭哧吭哧地编写那些协议解析的代码了(⼲这种活是典型的吃⼒不讨好)

4.ProtoBuf 安装

⼀、ProtoBuf 在 window 下的安装

1、下载 ProtoBuf 编译器

下载地址: https://github.com/protocolbuffers/protobuf/releases
下载之后将压缩包解压到本地⽬录下。解压后的⽂件内包含 bin、include⽂件,以及⼀个
readme.txt。

2、配置环境变量

把解压后⽂件中的bin⽬录配置到系统环境变量的Path中去

⼆、ProtoBuf 在 Linux 下的安装

下载 ProtoBuf 前⼀定要安装依赖库:autoconf automake libtool curl make g++ unzip

如未安装,安装命令如下:
Ubuntu ⽤⼾选择:
1 sudo apt-get install autoconf automake libtool curl make g++ unzip -y
CentOS ⽤⼾选择:
1 sudo yum install autoconf automake libtool curl make gcc-c++ unzip
在这⾥我们希望⽀持全部语⾔,所以选择 protobuf-all-21.11.zip,右键将下载链接复制出来。
下载完成后,解压zip包:
unzip protobuf-all-21.11.zip
解压完成后,会⽣成 protobuf-21.11 ⽂件,进⼊⽂件:
cd protobuf-21.11

2、安装 ProtoBuf

进⼊解压好的⽂件,执⾏以下命令
# 第⼀步执⾏ autogen.sh 但如果下载的是具体的某⼀⻔语⾔,不需要执⾏这⼀步。
./autogen.sh
# 第⼆步执⾏ configure ,有两种执⾏⽅式,任选其⼀即可,如下:
# 1 protobuf 默认安装在 /usr/local ⽬录, lib bin 都是分散的
./configure
# 2 修改安装⽬录,统⼀安装在 /usr/local/protobuf
./configure --prefix=/usr/local/protobuf
make // 执⾏ 15 分钟左右
make check // 执⾏ 15 分钟左右
sudo make install
3、检查是否安装成功
输⼊ protoc --version 查看版本,有显⽰说明安装成功。
1 hyb@139-159-150-152:~/install/protobuf-21.11$ protoc --version
2 libprotoc 3.21.11

3.快速上⼿

在快速上⼿中,会编写第⼀版本的通讯录 1.0。在通讯录 1.0 版本中,将实现:
对⼀个联系⼈的信息使⽤ PB 进⾏序列化,并将结果打印出来。
对序列化后的内容使⽤ PB 进⾏反序列,解析出联系⼈信息并打印出来。
联系⼈包含以下信息: 姓名、年龄。
通过通讯录 1.0,我们便能了解使⽤ ProtoBuf 初步要掌握的内容,以及体验到 ProtoBuf 的完整使⽤流 程。

步骤1:创建 .proto ⽂件

1.⽂件规范
1.  创建 .proto ⽂件时,⽂件命名应该使⽤全⼩写字⺟命名,多个字⺟之间⽤ _ 连接。 例如:
lower_snake_case.proto
2.  书写 .proto ⽂件代码时,应使⽤ 2 个空格的缩进。
我们为通讯录 1.0 新建⽂件: contacts.proto
2.添加注释
向⽂件添加注释,可使⽤ // 或者 /* ... */
3.指定 proto3 语法
Protocol Buffers 语⾔版本3,简称 proto3,是 .proto ⽂件最新的语法版本。proto3 简化了 Protocol Buffers 语⾔,既易于使⽤,⼜可以在更⼴泛的编程语⾔中使⽤。它允许你使⽤ Java,C++,Python 等多种语⾔⽣成 protocol buffer 代码。
在 .proto ⽂件中,要使⽤ syntax = "proto3"; 来指定⽂件语法为 proto3,并且必须写在除去
注释内容的第⼀⾏。 如果没有指定,编译器会使⽤proto2语法。
在通讯录 1.0 的 contacts.proto ⽂件中,可以为⽂件指定 proto3 语法
4.package 声明符
package 是⼀个可选的声明符,能表⽰ .proto ⽂件的命名空间,在项⽬中要有唯⼀性。它的作⽤是为 了避免我们定义的消息出现冲突。
在通讯录 1.0 的 contacts.proto ⽂件中,可以声明其命名空间,内容如下:
5.定义消息(message)
消息(message): 要定义的结构化对象,我们可以给这个结构化对象中定义其对应的属性内容。
这⾥再提⼀下为什么要定义消息?
在⽹络传输中,我们需要为传输双⽅定制协议。定制协议说⽩了就是定义结构体或者结构化数据,
⽐如,tcp,udp 报⽂就是结构化的。 再⽐如将数据持久化存储到数据库时,会将⼀系列元数据统⼀⽤对象组织起来,再进⾏存储。
所以 ProtoBuf 就是以 message 的⽅式来⽀持我们定制协议字段,后期帮助我们形成类和⽅法来使
⽤。在通讯录 1.0 中我们就需要为 联系⼈ 定义⼀个 message。
定义消息字段
在 message 中我们可以定义其属性字段,字段定义格式为:字段类型 字段名 = 字段唯⼀编号
字段名称命名规范:全⼩写字⺟,多个字⺟之间⽤ _ 连接。
字段类型分为:标量数据类型 和 特殊类型(包括枚举、其他消息类型等)。
字段唯⼀编号:⽤来标识字段,⼀旦开始使⽤就不能够再改变。
该表格展⽰了定义于消息体中的标量数据类型,以及编译 .proto ⽂件之后⾃动⽣成的类中与之对应的 字段类型。在这⾥展⽰了与 C++ 语⾔对应的类型。
在这⾥还要特别讲解⼀下字段唯⼀编号的范围:
1 ~ 536,870,911 (2^29 - 1) ,其中 19000 ~ 19999 不可⽤。
19000 ~ 19999 不可⽤是因为:在 Protobuf 协议的实现中,对这些数进⾏了预留。如果⾮要在.proto ⽂件中使⽤这些预留标识号,例如将 name 字段的编号设置为19000,编译时就会报警:
值得⼀提的是,范围为 1 ~ 15 的字段编号需要⼀个字节进⾏编码, 16 ~ 2047 内的数字需要两个字节 进⾏编码。编码后的字节不仅只包含了编号,还包含了字段类型。所以 1 ~ 15 要⽤来标记出现⾮常频 繁的字段,要为将来有可能添加的、频繁出现的字段预留⼀些出来。

步骤2:编译 contacts.proto ⽂件,⽣成 C++ ⽂件

编译命令
编译命令⾏格式为:
编译 contacts.proto ⽂件后会⽣成什么
编译 contacts.proto ⽂件后,会⽣成所选择语⾔的代码,我们选择的是C++,所以编译后⽣成了两个 ⽂件: contacts.pb.h contacts.pb.cc
对于编译⽣成的 C++ 代码,包含了以下内容 :
对于每个 message ,都会⽣成⼀个对应的消息类。
在消息类中,编译器为每个字段提供了获取和设置⽅法,以及⼀下其他能够操作字段的⽅法。
编辑器会针对于每个 .proto ⽂件⽣成 .h .cc ⽂件,分别⽤来存放类的声明与类的实现。
上述的例⼦中:
每个字段都有设置和获取的⽅法, getter 的名称与⼩写字段完全相同,setter ⽅法以 set_ 开头。
每个字段都有⼀个 clear_ ⽅法,可以将字段重新设置回 empty 状态。
contacts.pb.cc 中的代码就是对类声明⽅法的⼀些实现,在这⾥就不展开了。
到这⾥大家可能就有疑惑了,那之前提到的序列化和反序列化⽅法在哪⾥呢?在消息类的⽗类
MessageLite 中,提供了读写消息实例的⽅法,包括序列化⽅法和反序列化⽅法。
注意:
序列化的结果为⼆进制字节序列,⽽⾮⽂本格式。
以上三种序列化的⽅法没有本质上的区别,只是序列化后输出的格式不同,可以供不同的应⽤场景 使⽤。
序列化的 API 函数均为const成员函数,因为序列化不会改变类对象的内容, ⽽是将序列化的结果 保存到函数⼊参指定的地址中。
步骤3:序列化与反序列化的使⽤
创建⼀个测试⽂件 main.cc,⽅法中我们实现:
对⼀个联系⼈的信息使⽤ PB 进⾏序列化,并将结果打印出来。
对序列化后的内容使⽤ PB 进⾏反序列,解析出联系⼈信息并打印出来
代码书写完成后,编译 main.cc,⽣成可执⾏程序 TestProtoBuf
1 g++ main.cc contacts.pb.cc -o TestProtoBuf -std=c++11 -lprotobuf
-lprotobuf:必加,不然会有链接错误。
-std=c++11:必加,使⽤C++11语法
执⾏ test  ,可以看⻅ people 经过序列化和反序列化后的结果:
由于 ProtoBuf 是把联系⼈对象序列化成了⼆进制序列,这⾥⽤ string 来作为接收⼆进制序列的容器。 所以在终端打印的时候会有换⾏等⼀些乱码显⽰。
所以相对于 xml 和 JSON 来说,因为被编码成⼆进制,破解成本增⼤,ProtoBuf 编码是相对安全的。

⼩结 ProtoBuf 使⽤流程

1. 编写 .proto ⽂件,⽬的是为了定义结构对象(message)及属性内容。
2. 使⽤ protoc 编译器编译 .proto ⽂件,⽣成⼀系列接⼝代码,存放在新⽣成头⽂件和源⽂件中。
3. 依赖⽣成的接⼝,将编译⽣成的头⽂件包含进我们的代码中,实现对 .proto ⽂件中定义的字段进⾏ 设置和获取,和对 message 对象进⾏序列化和反序列化。
总的来说:ProtoBuf 是需要依赖通过编译⽣成的头⽂件和源⽂件来使⽤的。有了这种代码⽣成机制, 开发⼈员再也不⽤吭哧吭哧地编写那些协议解析的代码了(⼲这种活是典型的吃⼒不讨好)。

五、proto 3 语法详解

在语法详解部分,依旧使⽤ 项⽬推进 的⽅式完成讲解。这个部分会对通讯录进⾏多次升级,使⽤ 2.x 表⽰升级的版本,最终将会升级如下内容:
不再打印联系⼈的序列化结果,⽽是将通讯录序列化后并写⼊⽂件中。
从⽂件中将通讯录解析出来,并进⾏打印。
新增联系⼈属性,共包括:姓名、年龄、电话信息、地址、其他联系⽅式、备注。

1. 字段规则

消息的字段可以⽤下⾯⼏种规则来修饰:
singular :消息中可以包含该字段零次或⼀次(不超过⼀次)。 proto3 语法中,字段默认使⽤该
规则。
repeated :消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理
解为定义了⼀个数组。
更新 contacts.proto , PeopleInfo 消息中新增 phone_numbers 字段,表⽰⼀个联系⼈有多个
号码,可将其设置为 repeated,写法如下:

2. 消息类型的定义与使⽤

2.1 定义
在单个 .proto ⽂件中可以定义多个消息体,且⽀持定义嵌套类型的消息(任意多层)。每个消息体中 的字段编号可以重复。
更新 contacts.proto,我们可以将 phone_number 提取出来,单独成为⼀个消息:
//嵌套写法
syntax = "proto3";
package contacts;
message PeopleInfo {
 string name = 1; 
 int32 age = 2; 
 message Phone {
 string number = 1;
 }
}
// -------------------------- ⾮嵌套写法 -------------------------
syntax = "proto3";
package contacts;
message Phone {
 string number = 1;
}
message PeopleInfo {
 string name = 1; 
 int32 age = 2; 
}

2.2 使⽤

消息类型可作为字段类型使⽤
contacts.proto
syntax = "proto3";
package contacts;
// 联系⼈
message PeopleInfo {
 string name = 1; 
 int32 age = 2; 
 message Phone {
 string number = 1; 
 }
 repeated Phone phone = 3; 
}
可导⼊其他 .proto ⽂件的消息并使⽤

syntax = "proto3";
package contacts;
import "phone.proto"; // 使⽤ import 将 phone.proto ⽂件导⼊进来 !!!
message PeopleInfo {
 string name = 1; 
 int32 age = 2; 
 
 // 引⼊的⽂件声明了package,使⽤消息时,需要⽤ ‘命名空间.消息类型’ 格式 
 
 repeated phone.Phone phone = 3; 
}
注:在 proto3 ⽂件中可以导⼊ proto2 消息类型并使⽤它们,反之亦然

2.3.1 通讯录 2.0 的写⼊实现

write.cc (通讯录 2.0)
#include<iostream>
#include<fstream>
#include"contact.pb.h"
using namespace std;

void AddPeopleInfo(contacts::PeopleInfo* info)
{
    info->set_name("lisi");
    info->set_age(18);
    phone::Phone* newphone=info->add_phone();
    newphone->set_numbers("1234567890");
}

int main()
{
    contacts::Contacts contacter;
    
    fstream input("contacts.bin",ios::in | ios::binary);
    if(!input)
    {
        cerr<<"Creat A New File"<<endl;
    }
    else if(!contacter.ParseFromIstream(&input)) 
    {
        cerr<<"Parse Error"<<endl;
        input.close();
        return -1;
    }

    AddPeopleInfo(contacter.add_people());

    fstream output("contacts.bin",ios::out|ios::binary|ios::trunc);
    if(!contacter.SerializeToOstream(&output))
    {
        cerr<<"write error"<<endl;
        input.close();
        output.close();
        return -1;
    }

    cout<<"write success"<<endl;

    return 0;
}

2.3.2 通讯录 2.0 的读取实现

read.cc (通讯录 2.0)
#include<iostream>
#include<fstream>
#include"contact.pb.h"
using namespace std;
int main()
{
    contacts::Contacts contacter;
    
    fstream input("contacts.bin",ios::in | ios::binary);
    if(!contacter.ParseFromIstream(&input)) 
    {
        cerr<<"Parse Error"<<endl;
        input.close();
        return -1;
    }

    for(int i=0;i<contacter.people_size();i++)
    {
        const contacts::PeopleInfo& people=contacter.people(i);
        cout<<people.name()<<":"<<people.age()<<":";
        for(int j=0;j<people.phone_size();j++)
        {
            const phone::Phone& numer=people.phone(j);
            cout<<numer.numbers()<<":";
        }
        cout<<endl;
    }

    return 0;
}

3. enum 类型

3.1 定义规则

语法⽀持我们定义枚举类型并使⽤。在.proto⽂件中枚举类型的书写规范为:
枚举类型名称:
使⽤驼峰命名法,⾸字⺟⼤写。 例如: MyEnum
常量值名称:
全⼤写字⺟,多个字⺟之间⽤ _ 连接。例如: ENUM_CONST = 0;
我们可以定义⼀个名为 PhoneType 的枚举类型,定义如下
要注意枚举类型的定义有以下⼏种规则:
1. 0 值常量必须存在,且要作为第⼀个元素。这是为了与 proto2 的语义兼容:第⼀个元素作为默认 值,且值为 0。
2. 枚举类型可以在消息外定义,也可以在消息体内定义(嵌套)。
3. 枚举的常量值在 32 位整数的范围内。但因负值⽆效因⽽不建议使⽤(与编码规则有关)。

3.2 定义时注意

将两个 ‘具有相同枚举值名称’ 的枚举类型放在单个 .proto ⽂件下测试时,编译后会报错:某某某常
量已经被定义!所以这⾥要注意:
同级(同层)的枚举类型,各个枚举类型中的常量不能重名。
单个 .proto ⽂件下,最外层枚举类型和嵌套枚举类型,不算同级。
多个 .proto ⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都未声明 package,每个 proto ⽂
件中的枚举类型都在最外层,算同级。
多个 .proto ⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都声明了 package,不算同级
我们继续更新我们的项目
write.cc当中的修改
read.cc的修改

4.Any 类

字段还可以声明为 Any 类型,可以理解为泛型类型。使⽤时可以在 Any 中存储任意消息类型。
型的字段也⽤ repeated 来修饰。
Any 类型是 google 已经帮我们定义好的类型,在安装 ProtoBuf 时,其中的 include ⽬录下查找所有 google 已经定义好的 .proto ⽂件。
PackFrom可以将其他类转化成Any类。
unpackto则可以将Any类还原。

5. oneof 类型

如果消息中有很多可选字段, 并且将来同时只有⼀个字段会被设置, 那么就可以使⽤ oneof 加强这 个⾏为,也能有节约内存的效果。
升级write.cc
升级read.cc
注意:
可选字段中的字段编号,不能与⾮可选字段的编号冲突。
不能在 oneof 中使⽤ repeated 字段。
将来在设置 oneof 字段中值时,如果将 oneof 中的字段设置多个,那么只会保留最后⼀次设置的成 员,之前设置的 oneof 成员会⾃动清除。

6. map 类型

语法⽀持创建⼀个关联映射字段,也就是可以使⽤ map 类型去声明字段类型,格式为:
map<key_type, value_type> map_field = N;
要注意的是:
key_type 是除了 float 和 bytes 类型以外的任意标量类型。 value_type 可以是任意类型。
map 字段不可以⽤ repeated 修饰
map 中存⼊的元素是⽆序的
更新write.cc
更新read.cc

7. 默认值

反序列化消息时,如果被反序列化的⼆进制序列中不包含某个字段,反序列化对象中相应字段时,就 会设置为该字段的默认值。不同的类型对应的默认值不同:
对于字符串,默认值为空字符串。
对于字节,默认值为空字节。
对于布尔值,默认值为 false。
•  对于数值类型,默认值为 0。
对于枚举,默认值是第⼀个定义的枚举值, 必须为 0。
对于消息字段,未设置该字段。它的取值是依赖于语⾔。
对于设置了 repeated 的字段的默认值是空的(
通常是相应语⾔的⼀个空列表 )。
对于 消息字段 、 oneof 字段 和 any 字段 ,C++ 和 Java 语⾔中都有 has_ ⽅法来检测当前字段
是否被设置。

8. 更新消息

8.1 更新规则

如果现有的消息类型已经不再满⾜我们的需求,例如需要扩展⼀个字段,在不破坏任何现有代码的情 况下更新消息类型⾮常简单。遵循如下规则即可:
禁⽌修改任何已有字段的字段编号。
若是移除⽼字段,要保证不再使⽤移除字段的字段编号。正确的做法是保留字段编号
(reserved),以确保该编号将不能被重复使⽤。不建议直接删除或注释掉字段。
int32, uint32, int64, uint64 和 bool 是完全兼容的。可以从这些类型中的⼀个改为另⼀个,
⽽不破坏前后兼容性。若解析出来的数值与相应的类型不匹配,会采⽤与 C++ ⼀致的处理⽅案
(例如,若将 64 位整数当做 32 位进⾏读取,它将被截断为 32 位)。
sint32 和 sint64 相互兼容但不与其他的整型兼容。
string 和 bytes 在合法 UTF-8 字节前提下也是兼容的。
bytes 包含消息编码版本的情况下,嵌套消息与 bytes 也是兼容的。
fixed32 与 sfixed32 兼容, fixed64 与 sfixed64兼容。
enum 与 int32,uint32, int64 和 uint64 兼容(注意若值不匹配会被截断)。但要注意当反序
列化消息时会根据语⾔采⽤不同的处理⽅案:例如,未识别的 proto3 枚举类型会被保存在消息
中,但是当消息反序列化时如何表⽰是依赖于编程语⾔的。整型字段总是会保持其的值。
oneof:
将⼀个单独的值更改为 新 oneof 类型成员之⼀是安全和⼆进制兼容的。
若确定没有代码⼀次性设置多个值那么将多个字段移⼊⼀个新 oneof 类型也是可⾏的。
将任何字段移⼊已存在的 oneof 类型是不安全的。

8.2 保留字段 reserved

如果通过 删除 或 注释掉 字段来更新消息类型,未来的⽤⼾在添加新字段时,有可能会使⽤以前已经 存在,但已经被删除或注释掉的字段编号。将来使⽤该 .proto 的旧版本时的程序会引发很多问题:数 据损坏、隐私错误等等。
确保不会发⽣这种情况的⼀种⽅法是:使⽤ reserved 将指定字段的编号或名称设置为保留项 。当
我们再使⽤这些编号或名称时,protocol buffer 的编译器将会警告这些编号或名称不可⽤。举个例
⼦:

8.3 未知字段

未知字段:解析结构良好的 protocol buffer 已序列化数据中的未识别字段的表⽰⽅式。例如,当
旧程序解析带有新字段的数据时,这些新字段就会成为旧程序的未知字段。
本来,proto3 在解析消息时总是会丢弃未知字段,但在 3.5 版本中重新引⼊了对未知字段的保留机
制。所以在 3.5 或更⾼版本中,未知字段在反序列化时会被保留,同时也会包含在序列化的结果
中。

8.4 前后兼容性

根据上述的例⼦可以得出,pb是具有向前兼容的。为了叙述⽅便,把增加了“⽣⽇”属性的 service
称为“新模块”;未做变动的 client 称为 “⽼模块”。
向前兼容:⽼模块能够正确识别新模块⽣成或发出的协议。这时新增加的“⽣⽇”属性会被当作未
知字段(pb 3.5版本及之后)。
向后兼容:新模块也能够正确识别⽼模块⽣成或发出的协议。
前后兼容的作⽤:当我们维护⼀个很庞⼤的分布式系统时,由于你⽆法同时 升级所有 模块,为了保证
在升级过程中,整个系统能够尽可能不受影响,就需要尽量保证通讯协议的“向后兼容”或“向前兼
容”。

9. 选项 option

.proto ⽂件中可以声明许多选项,使⽤ option 标注。选项能影响 proto 编译器的某些处理⽅式。
选项分为 ⽂件级、消息级、字段级 等等, 但并没有⼀种选项能作⽤于所有的类型

9.1 常⽤选项列举

optimize_for : 该选项为⽂件选项,可以设置 protoc 编译器的优化级别,分别为 SPEED
CODE_SIZE LITE_RUNTIME 。受该选项影响,设置不同的优化级别,编译 .proto ⽂件后⽣
成的代码内容不同。
SPEED : protoc 编译器将⽣成的代码是⾼度优化的,代码运⾏效率⾼,但是由此⽣成的代码
编译后会占⽤更多的空间。 SPEED 是默认选项。
CODE_SIZE : proto 编译器将⽣成最少的类,会占⽤更少的空间,是依赖基于反射的代码来
实现序列化、反序列化和各种其他操作。但和 SPEED 恰恰相反,它的代码运⾏效率较低。这
种⽅式适合⽤在包含⼤量的.proto⽂件,但并不盲⽬追求速度的应⽤中。
LITE_RUNTIME : ⽣成的代码执⾏效率⾼,同时⽣成代码编译后的所占⽤的空间也是⾮常
少。这是以牺牲Protocol Buffer提供的反射功能为代价的,仅仅提供 encoding+序列化 功能,
所以我们在链接 BP 库时仅需链接libprotobuf-lite,⽽⾮libprotobuf。这种模式通常⽤于资源
有限的平台,例如移动⼿机平台中
allow_alias : 允许将相同的常量值分配给不同的枚举常量,⽤来定义别名。该选项为枚举选项。

9.2设置⾃定义选项

ProtoBuf 允许⾃定义选项并使⽤。该功能⼤部分场景⽤不到,在这⾥不拓展讲解。
有兴趣可以参考: https://developers.google.cn/protocol-buffers/docs/proto?hl=zh
cn#customoptions
至此大家应该就已经能够基本使用protobuf了,谢谢大家观看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2137759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot环境下的读书笔记交流网络

第2章 技术介绍 2.1B/S架构 当向其他用户发送请求的功能时应用B/S模式具有独一无二的优点&#xff1a;用户请求通过网络向其他Web服务器发送时只需要通过浏览器就可以实现该功能。该功能的好处之一就是有效简化了客户端&#xff0c;大部分开发的软件只需要用浏览器即可&#xf…

QT Mode/View之View

目录 概念 使用已存在的视图 使用模型 使用模型的多个视图 处理元素的选择 视图间共享选择 概念 在模型/视图架构中&#xff0c;视图从模型中获取数据项并将它们呈现给用户。数据的表示方式不必与模型提供的数据表示形式相似&#xff0c;而且可能与用于存储数据项的底层数…

【重学 MySQL】二十四、笛卡尔积的错误和正确的多表查询

【重学 MySQL】二十四、笛卡尔积的错误和正确的多表查询 笛卡尔积的理解和错误笛卡尔积的理解定义例子在数据库中的应用总结 笛卡尔积的错误 正确的多表查询使用 INNER JOIN使用 WHERE 子句&#xff08;隐式内连接&#xff09; 总结 在数据库查询中&#xff0c;特别是涉及到多表…

【python计算机视觉编程——9.图像分割】

python计算机视觉编程——9.图像分割 9.图像分割9.1 图割安装Graphviz下一步&#xff1a;正文9.1.1 从图像创建图9.1.2 用户交互式分割 9.2 利用聚类进行分割9.3 变分法 9.图像分割 9.1 图割 可以选择不装Graphviz&#xff0c;因为原本觉得是要用&#xff0c;后面发现好像用不…

大模型教程:使用 Milvus、vLLM 和 Llama 3.1 搭建 RAG 应用

vLLM 是一个简单易用的 LLM 推理服务库。加州大学伯克利分校于 2024 年 7 月将 vLLM 作为孵化项目正式捐赠给 LF AI & Data Foundation 基金会。欢迎 vLLM 加入 LF AI & Data 大家庭&#xff01;&#x1f389; 在主流的 AI 应用架构中&#xff0c;大语言模型&#xff…

数据清洗-缺失值处理-缺失值可视化图(竖线)

目录 一、安装所需的python包二、缺失值可视化分析2.1 可直接运行代码2.2 以某个缺失值数据进行可视化实战2.2.1 代码运行过程截屏&#xff1a;2.2.2 缺失图可视化&#xff1a; 感觉大家对原理性的东西不太感兴趣&#xff0c;那我就直接举例提供代码&#xff0c;以及详细的注释…

13、Python如何设置文件缓冲

什么是I/O操作&#xff0c;看一下百度百科的说法&#xff1a;I/O操作是指对设备与cpu连接的接口电路的操作&#xff0c;不是对外围设备直接进行操作。宏观上讲&#xff0c;I/O是信息处理系统&#xff08;例如计算机&#xff09;与外部世界&#xff08;可能是人或其他信息处理系…

Flutter之SystemChrome全局设置

一、简介 SystemChrome作为一个全局属性&#xff0c;很像 Android 的 Application&#xff0c;功能很强大。 二、使用详解 2.1 setPreferredOrientations 设置屏幕方向 在我们日常应用中可能会需要设置横竖屏或锁定单方向屏幕等不同要求&#xff0c;通过 setPreferredOrien…

阿里云镜像报错 [Errno 14] HTTP Error 302 - Found 问题解决记录

1、问题背景和解决思路 在本地安装 CentOS7 后&#xff0c;网络已调通可正常上网&#xff0c;但切换阿里云镜像后&#xff0c;使用 yum 安装软件时出现 “[Errno 14] HTTPS Error 302 - Found Trying other mirror.” 报错&#xff0c;原因是 yum 源配置问题。给出了详细的解决…

分布式可信认证:数据安全与隐私保护新范式

文章目录 前言一、可信数字身份成数据要素流通的关键二、分布式可信认证成数据安全与隐私保护新范式1、分布式可信认证很好地解决传统数字身份认证的痛点问题2、可信数字身份上升为国家战略三、安全是未来数字身份的基础1、有效的威胁建模策略是确保这些系统安全性的基石前言 …

数据清洗-缺失值填充-随机森林搜寻最优参数填充

目录 一、安装所需的python包二、采用随机森林算法进行缺失值填充2.1可直接运行代码2.2以某个缺失值数据进行实战代码运行过程截屏&#xff1a;填充后的数据截屏&#xff1a; 三、随机森林算法 (Random Forest) 介绍3.1随机森林的定义3.2随机森林的基本思想3.3随机森林的工作原…

语言模型中的多模态链式推理(论文复现)

语言模型中的多模态链式推理&#xff08;论文复现&#xff09; 本文所涉及所有资源均在传知代码平台可获取 文章目录 语言模型中的多模态链式推理&#xff08;论文复现&#xff09;简介摘要引言多模态思维链推理的挑战多模态CoT框架多模态CoT模型架构细节编码模块融合模块解码模…

redis短信登录模型

基于Session实现登录 &#xff0c;

Unity程序基础框架

概述 单例模式基类 没有继承 MonoBehaviour 继承了 MonoBehaviour 的两种单例模式的写法 缓存池模块 &#xff08;确实挺有用&#xff09; using System.Collections; using System.Collections.Generic; using UnityEngine;/// <summary> /// 缓存池模块 /// 知识点 //…

数据结构基础详解:哈希表【C语言代码实践篇】开放地址法__拉链法_哈希表的创建_增删查操作详解

文章目录 1.哈希表代码实现之开放地址法1.1 开放地址法创建哈希表1.2 开放地址法之查找1.3 开放地址法之插入1.4 开放地址法之删除 2.哈希表代码实现之链地址法(拉链法)2.1 链地址法之创建哈希表2.2 链地址法之查找2.3 链地址法之插入2.4 链地址法之删除 1.哈希表代码实现之开放…

Stable diffusion生图原理

简介 Stable diffusion 是一种基于扩散技术的深度学习模型&#xff0c;于2022年发布&#xff0c;是Stability AI公司推出的首要产品&#xff0c;它主要用于生成以文本描述为条件的详细图像&#xff0c;同时也可以进行补绘、外绘、重绘等任务&#xff0c;但原理都和文生图原理…

C++中矩阵的介绍及相关应用扩展详解

1. 矩阵概念 在数学中&#xff0c;矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集合&#xff0c;最早来自于方程组的系数及常数所构成的方阵。这一概念由19世纪英国数学家凯利首先提出。 矩阵是高等代数学中的常见工具&#xff0c;也常见于统计分析…

Qt-QPushButton按钮类控件(22)

目录 描述 使用 给按钮添加图片 给按钮添加快捷键 添加槽函数 添加快捷键 添加组合键 开启鼠标的连发功能 描述 经过上面的一些介绍&#xff0c;我们也尝试的使用过了这个控件&#xff0c;接下来我们就要详细介绍这些比较重要的控件了 使用 给按钮添加图片 我们创建…

线性表之单链表

在上一节我们学习了线性表中的顺序表&#xff0c;今天我们来学习一下线性表中的另一种结构——单链表 前言 我们在之前已经初步了解了数据结构中的两种逻辑结构&#xff0c;但线性结构中并非只有顺序表一种&#xff0c;它还有不少兄弟姐妹&#xff0c;今天我们再来学习一下单链…

RealityCapture全面讲解:摄影测量软件的新纪元

随着数字化技术的迅猛发展&#xff0c;摄影测量软件在各行各业中的应用日益广泛。其中&#xff0c;RealityCapture作为一款领先的摄影测量解决方案&#xff0c;以其卓越的速度、精度和易用性&#xff0c;赢得了全球众多专业人士的青睐。本文将全面讲解RealityCapture的功能特点…