protobuf协议使用详解

一、protobuf协议详解

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

systax = "proto3"; //表明使用proto3语法;如果你没有指定这个,编译器会使用proto2语法;这个指定语法行必须是文件的非空非注释的第一个行
package School; //包名,类似于模块

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:表示是一个必须字段 ,必须相对于发送方,在发送消息之前必须设置该字段的值,对于接收方,必须能够识别该字段的意思。发送之前没有设置required字段或者无法识别required字段都会引发编解码异常,导致消息被丢弃。

optional:表示是一个可选字段 ,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。

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


②. 数据类型

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

protobuf 数据结构描述打包C++语言映射
bool布尔类型1字节bool
double64浮点数Ndouble
float32浮点数Nfloat
int3232位整数Nint
uint32无符号32位整数Nunsigned int
int6464位整数N__int64
uint6464位无整数Nunsigned __int64
sint3232位整数,处理负数效率更高Nint32
sint6464位整数,处理负数效率更高N__int64
fixed3232位无符号整数4unsigned int32
fixed6464位无符号整数8unsigned __int64
sfixed3232位整数,能以更高的效率处理负数4unsigned int32
sfixed6464位整数8unsigned __int64
string只能处理ASCII字符Nstd::string
bytes用于处理多字节的语言字符,如中文Nstd::string
enum可以包含一个用户自定义的枚举类型uint32N(uint32)enum
message可以包含一个用户自定义的消息类型Nobject of class

N:表示打包的字节并不是固定的,而是根据数据的大小或者长度决定的


③. 字段名称

字段名称的命名与C、C++、Java等语言的变量命名方式几乎是相同的:字母、数字和下划线组成

protobuf建议字段的命名采用以下划线分割的驼峰式,例如:first_name 而不是firstName


④. 字段编码值

有了该值,通信双方才能互相识别对方的字段。当然相同的编码值,其限定修饰符和数据类型必须相同,编码值的取值范围为1~2^32

其中1~15的编码时间和空间效率都是最高的,编码值越大,其编码的时间和空间效率就越低(相对于1~15), protobuf 建议把经常要传递的值把其字段编码设置为1-15之间的值。

消息中的字段的编码值无需连续,只要是合法的,并且不能在同一个消息中有字段包含相同的编码值。

建议:项目投入运营以后涉及到版本升级时的新增消息字段全部使用optional或者repeated,尽量不实用required。如果使用了required,需要全网统一升级,如果使用optional或者repeated可以平滑升级。


⑤. 字段默认值

当在传递数据时,对于required数据类型,如果用户没有设置值,则使用默认值传递到对端。 对于optional字段,如果没有接收到optional字段,则设置为默认值。

对于strings,默认是一个空string

对于bytes,默认是一个空的bytes

对于bools,默认是false

对于数值类型,默认是0


二、使用message

//Demo.proto 协议格式文件
syntax='proto3'
package=Demo

message Data {
	optional int32 x = 1;
	optional string str = 2;
	repeated int32 d = 3;
}

2.1、类成员变量的访问

  • 获取成员变量:直接采用使用成员变量名(全部为小写);
  • 设置成员变量:使用成员变量名前加set_的方法
//使用message
#include <Demo.pb.h>
#include <QDebug>

Demo::Data data;
data.set_x(20); //设置成员变量
qDebug()<<data.x(); //获取成员变量

对于普通成员变量(required和optional)

  • 提供has_方法判断变量值是否被设置;
  • 提供clear_方法清除设置的变量值 ;
//使用message
#include <Demo.pb.h>
#include <QDebug>

Demo::Data data;
data.set_x(20); //设置成员变量
qDebug()<<data.has_x(); //判断变量值是否被设置
data.clear_x(); //清除x设置的变量值

对于string类型

  • 提供了多种set_方法,其参数不同;
  • 提供了一个mutable_方法,返回变量值的可修改指针 ;
//使用message
#include <Demo.pb.h>
#include <QDebug>

Demo::Data data;
data.set_str(20); //设置成员变量
std::string* mutable_str(); //返回str变量值的可修改指针

对于repeated变量

  • _size方法:返回变量的长度;
  • 通过下脚标访问其中的数据成员组;
  • 通过下脚标返回其中的成员的mutable_的方法
  • _add方法:增加一个成员
//使用message
#include <Demo.pb.h>
#include <QDebug>

Demo::Data data;
for(int i=0; i<10; i++)
{
    data.d_add(i); //向d中添加成员
}
for(int i=0; i<data.d_size(); i++)
    printf("%d\t",data.d(i)); //通过下脚标访问数据成员组

三、序列化和反序列化

3.1、序列化和反序列化有什么用?

序列化和反序列化主要用在保存数据结构上,保存数据很简单,各种形式都可以,例如txt,但是如果想把数据恢复成原先的数据结构就没那么简单了。

例如:下面是一个学生的结构体

struct student {
	QString name;
	QString class;
	long int stu_id;
	float Chinese;
	float Math;
	float English;
}

假如把三年一班的学生记录为一个结构体数组struct student Class_3_1[max_length];
把它存为.txt文件,如果想把它恢复成struct student Class_3_1[max_length];就得自己解析文件,特别麻烦。

如果采用序列化可以把数据结构序列化为二进制数据进行存储,反序列化可以把存储的二进制数据再次恢复成之前的数据结构,很方便使用。

3.2、序列化

//文件后缀可以自定
fileName = QFileDialog::getSaveFileName(0, QObject::tr("protobuf序列化"),currentPath,QObject::tr("TestData(*.TD)"));

if (!fileName.isEmpty())
{
	if (!fileName.endsWith(".TD"))
	{
		fileName += ".TD";
	}
	
	QFile file(fileName);  
	if(file.open(QIODevice::WriteOnly))  
	{  
		// data 是一个 Demo::Data message对象
		int nLength = data->ByteSize(); 
		char* pbuf = new char[nLength];
		data->SerializePartialToArray(pbuf,nLength); //序列化
		if(nLength == file.write(pbuf,nLength))
		{
			qDebug()<<"SAVE_SUCESS";
		}
		else
		{
			qDebug()<<"SAVE_FAIL";
		}
	}
	file.close();
}

3.3、反序列化

fileName = QFileDialog::getOpenFileName(0, QObject::tr("读取参考曲线数据 "),sCurPath,QObject::tr("ReferenceLine(*.RL)"));

Demo::Data data_1;

if (!fileName.isEmpty())
{	
	QFile file(fileName);  
	if(file.open(QIODevice::ReadOnly))  
	{  
		QByteArray array_para = file.readAll();
		int nLen = array_para.length();
		// data 是一个 Demo::Data message对象
		if(!data_1->ParsePartialFromArray(array_para.data(),nLen)) //反序列化
		{
			qDebug()<<"LOAD_FAIL";
		}
		else
		{
			qDebug()<<"LOAD_SUCCESS";
			
			int X = data_1->x();
			QString STR = QString::fromUtf8(data_1->str().data());
			QVector<int> D;
			for(int i=0; i<data_1->d_size(); i++)
			{
				D.push_back(data_1->d(i));
			}
		}	
	}
	file.close();
}
引用[1]:protoc是proto文件的编译器,可以将proto文件编译成C、Java、Python三种代码文件。编译命令的格式如下:protoc -I=$SRC_DIR --cpp_out=$DST_DIR /path/to/file.proto。[1] 引用[2]:使用proto编译工具可以根据.proto文件生成对应语言的源文件,比如生成C++的.cc和.h文件。在调用接口时,需要添加宏GOOGLE_PROTOBUF_VERIFY_VERSION,并实现序列化和反序列化的函数接口。[2] 引用[3]:在.proto文件中,我们可以定义message和enum来表示数据结构。message用于表示一条消息,enum用于表示枚举类型。使用protobuf编译器编译协议文件后,会生成对应的类。如果某个字段可能出现多种不同类型,可以使用oneof关键字。在.proto文件中,还可以指定字段的规则(required、optional、repeated)和类型(int32、int64、string等)。[3] 问题: protobuf使用在C语言中的详细解释是什么? 回答: 在C语言中使用protobuf,首先需要使用protoc编译器将.proto文件编译成C代码文件。编译命令的格式如下:protoc -I=$SRC_DIR --cpp_out=$DST_DIR /path/to/file.proto。然后,在调用接口时,需要添加宏GOOGLE_PROTOBUF_VERIFY_VERSION,并实现序列化和反序列化的函数接口。在.proto文件中,我们可以定义message和enum来表示数据结构。message用于表示一条消息,enum用于表示枚举类型。使用protobuf编译器编译协议文件后,会生成对应的C代码文件。在C代码中,可以使用生成的类来进行序列化和反序列化操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝勒里恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值