ProtoBuf学习

一、ProtoBuf协议

在protobuf中,协议时由一系列的消息(message)组成的,如下所示:

message 消息类型名
{
    限定修饰符  字段类型 字段名=字段唯一编号;
}
 message框架示例:
systax = "proto3"; //表明使用proto3语法;如果你没有指定这个,编译器会使用proto2语法;这个指定语法行必须是文件的非空非注释的第一个行
package School; //包名,类似于命名空间namespace

message Student { //消息,类似于类
	required string name = 1 [default="张三"];
	optional int32 chinese = 2 [default=0];
	optional int32 math = 3 [default=0];
	optional int32 english = 4 [default=0];
}

message Teacher {
	required strint name = 1;
	optional string class = 2;
	optional string object = 3; 
}

message HengShuiZhongXue {
	repeated Student student = 1; //message内可以嵌套message
	repeated Teacher teachar = 2;
}

字段格式

限定修饰符①|数据类型②|字段名称③=字段编码值④|字段默认值⑤

①.限定修饰符    required|optional|repeated

required: 必须字段;消息发送方,必须在发消息前设置该字段的值;消息接收方,必须能够识别该字段的意思;否则引发编解码异常,消息被丢弃。

optional:可选字段;消息发送方,可以设置或不设置该字段的值;消息接收方,如果能够识别可选字段就进行处理,无法识别则忽略该字段,其他字段正常处理;

repeated:该字段可以包含0~n个元素,其特性同optional,但每次可以包含多个值,可以看作一个数组

②数据类型

ProtoBuf定义了一套基本数据类型,几乎都可以映射到C++\Java等语言的基础数据类型。

protobuf数据结构描述打包C++语言映射
bool布尔类型1字节bool
float32位浮点数float
int3232位整数int
int 6464位整数int
string只能处理ASCII字符,使用中文的化需要转换成utf_8,,不然回报错std::string
bytes用于处理多字节的语言字符,如中文std::string
message可以包含一个用户自定义的消息类型object of class
RTKEY

RTDB_KEY_TYPE

③字段名称

由字母、数字和下划线组成;建议使用以下划线分割的驼峰式,如first_name而不是firstName。

④字段编码值
  • 通信双方通过该值相互识别对方的字段。相同的编码值,其限定修饰符和数据类型必须相同;编码值取值范围为1~2^32,其中1~15的编码时间和空间效率都是最高的,因此把要经常传递的值的字段编码设置为1~15之间的值。
  • 消息中的字段编码值无需联系,不能在同一个消息中有字段包含相同的编码值。
  • 新增消息的字段建议全部使用optional或者repeated,若使用required,需要全网统一升级。
⑤字段默认值

消息发送方:required数据类型,传递默认值到对端。

消息接收方:没收到optional字段,设置为默认值。

optional int32 num=1  [default=0]
数据类型默认值
strings空string
bytes空bytes
boolsfalse
数值类型0
枚举类型第一个值

二、编译

使用proto编译器,编译.proto文件,编译完成后自动生成.ph.cc.pb.h文件;

命令格式:

protoc [--proto_path=IMPORT_PATH] --cpp_out=OUT_DIR path/to/file.proto
/*
protoc:Protocol Buffer提供的命令行编译工具。
--proto_path:指定被编译的.proto文件所在目录,可写为 -I IMPORT_PATH。不指定此参数,在当前目录进行搜索;当某个.proto文件import其他.proto文件时,或需要编译的.proto文件不在当前目录,需要用-I来指定搜索目录。
--cpp_out:指编译后的文件为C++文件。
OUT_DIR:指编译后生成文件的目标路径
*/

eg1:
protoc -cpp_out = .contacts.proto  //在当前contacts.proto文件目录下执行
proto -I ./fast_start --cpp_out = ./fast_start/contacts.proto    //在contacts.proto文件父目录下执行。

其中包含了以下内容:

  • 编辑器会针对于每个 .proto ⽂件⽣成 .h 和 .cc ⽂件,分别⽤来存放类的声明与类的实现。
  • 对于每个message,都会⽣成⼀个对应的消息类。
  • 在消息类中,编译器为每个字段提供了获取和设置⽅法,以及⼀下其他能够操作字段的⽅法。

三、Protobuf中数据类型的读写

1.非嵌套  
syntax = "proto3";
package tutorial;
message Person 
{
	string name = 1;
	int32 id = 2;
	string email = 3;
}
tutorial::Person person;
person.set_name("Obama");
person.set_id(1234);
person.set_email("1234@qq.com");
2.嵌套  
syntax = "proto3";
package tutorial;
message PhoneNumber 
{
	string number = 1;
	PhoneType type = 2;
}

message Person 
{
	string name = 1;
	int32 id = 2;
	optional string email = 3;
	enum PhoneType {
		MOBILE = 0;
		HOME = 1;
		WORK = 2;
	}
	repeated PhoneNumber phones = 4;
}

 对PhoneNumber中number成员的赋值

1)使用set_allocated_传入指针

(1)一次赋值进来
tutorial::Person person;
tutorial::PhoneNumber *p_n=new tutorial::PhoneNumber;
p_n->set_number(100);
p_n->set_type();//此处是一层枚举类,若是多层消息类则一直.进去到最后(一般A.proto中的值set进B.proto中的某个值)
person.set_allocated_phone_number(p_n)//一次赋值进来
(2)一个个分别赋值
tutorial::Person person;
person.set_allocated_phone_number()->set_number(100);

2)使用mutable_

tutorial::Person person;
person.mutable_phone_number()->set_number(100);

3)也可以copyFrom 方法使用结构体结合mutable_赋值

tutorial::Person person;
tutorial::PhoneNumber p_n;
p_n->set_number(100);
p_n->set_type();
person.mutable_phone_number()->CopyFrom(p_n);
cout<<person.DebugString()<<endl;

5)当消息内容为Map类型时

package protoTest;
message DemoMsg
{
   map<int32,string>  a = 1;    
}

map 类型成员赋值需通过 mutable_xxx 方法进行:

protoTest::DemoMsg msg; 
msg.mutable_a()->insert({ 1,"aaaaa" });
msg.mutable_a()->insert({ 2,"bbbbb" });
msg.mutable_a()->insert({ 3,"ccccc" });   
cout << msg.DebugString() << endl;

map成员读取:

protoTest::DemoMsg msg; 
msg.mutable_a()->insert({ 1,"aaaaa" });
msg.mutable_a()->insert({ 2,"bbbbb" });   
auto map = msg.mutable_a();
if (map->contains(1))
{
    cout << map->at(1) << endl;
}

6)读取RTKEY类型数据

//读取RTKEY类型数据
RTDB_KEY_TYPE rtdb_temp;
rtdb_temp.key_id1=st_id.id1();
rtdb_temp.key_id2=st_id.id2();

四.repeated成员操作

syntax = "proto3";
package tutorial;

message Person 
{
	repeated PhoneNumber phones = 4;
}
message PhoneNumber 
{
	string number = 1;
	PhoneType type = 2;
}

1.赋值

add_<参数名>(参数值)

 eg1.

tutorial::Person person;
tutorial::PhoneNumber *phones=person.add_phones();
phones->set_number(100);

 eg2.

message Person {
  required int32 age = 1;
  required string name = 2;
}

message Family {
  repeated Person person = 1;
}
Family family;
Person* person;
 
// 添加一个家庭成员,John
person = family.add_person();
person->set_age(25);
person->set_name("John");
 
// 添加一个家庭成员,Lucy
person = family.add_person();
person->set_age(23);
person->set_name("Lucy");
 
// 添加一个家庭成员,Tony
person = family.add_person();
person->set_age(2);
person->set_name("Tony");
 
// 显示所有家庭成员
int size = family.person_size();
cout << "这个家庭有 " << size << " 个成员,如下:" << endl;
for(int i=0; i<size; i++)
{
	Person psn = family.person(i);
	cout << i+1 << ". " << psn.name() << ", 年龄 " << psn.age() << endl;
}
 

2.取出

 xxx_size()获取repeated标记的成员个数。

protoTest::DemoMsg msg; 
msg.add_a(0.2);
msg.add_a(0.5);   
int size = msg.a_size();
for (int i = 0; i < size; ++i)
{
   cout << msg.a(i) << " ";   //打印数组a中的全部元素
}

2.读取

④.元素删除

使用 mutable_xxx方法获取成员的指针,可以删除指定位置元素。

protoTest::DemoMsg msg;    
msg.add_a(0.2);
msg.add_a(0.5);    
msg.mutable_a()->erase(msg.mutable_a()->begin());  //删除了数组a中0.2的值
cout << msg.DebugString() << endl;

四.import导入其他proto文件定义的消息

开发一个项目的时候,通常很多消息定义都写在一个proto文件,不方便维护,通常会将消息定义写在不同的proto文件中,在需要的时候可以通过import导入其他proto文件定义的消息。

eg.

保存文件:result.proto

syntax = "proto3";
message Result   // Result消息定义
{
  string url = 1;
  string title = 2;
  repeated string snippets = 3; // 字符串数组类型
}

保存文件:serach_response.proto

syntax = "proto3";
import "result.proto";   // 导入Result消息定义

message SearchResponse    // 定义SearchResponse消息
{
  repeated Result results = 1; // 使用导入的Result消息
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!关于学习 Protocol Buffers(protobuf),您可以从以下几个方面入手: 1. 官方文档:首先,您可以查阅 Protocol Buffers 的官方文档,它提供了详细的说明、示例和教程,可以帮助您了解 protobuf 的基本概念和用法。官方文档地址为:https://developers.google.com/protocol-buffers/docs/overview。 2. 安装和使用:接下来,您可以按照官方文档中的指引,安装 Protocol Buffers 工具集,并学习如何使用 protobuf 编译器(protoc)生成指定语言的代码。您可以选择使用 C++、Java、Python 等支持 protobuf 的编程语言进行学习和实践。 3. 语法和消息定义:了解 protobuf 的语法和消息定义是非常重要的。您可以学习如何定义消息类型、字段和其他高级特性,以及如何使用扩展、嵌套消息等。官方文档中有详细的说明和示例,帮助您掌握这些概念。 4. 应用场景:除了基本的语法和消息定义,了解 protobuf 在实际应用中的使用也是很重要的。您可以了解如何在网络通信中使用 protobuf 进行数据传输,如何进行数据序列化和反序列化,以及如何处理不同语言之间的兼容性等问题。 5. 其他资源:除了官方文档,还有一些其他的学习资源可以帮助您更深入地学习和应用 protobuf。例如,可以阅读相关的书籍、博客文章,参与开源项目和社区讨论等。 希望以上信息对您有所帮助!如果您有其他问题,我会尽力回答。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值