菜鸟笔记
提升您的技术认知

protobuf协议使用详解-ag真人游戏

在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
double 64浮点数 n double
float 32浮点数 n float
int32 32位整数 n int
uint32 无符号32位整数 n unsigned int
int64 64位整数 n __int64
uint64 64位无整数 n unsigned __int64
sint32 32位整数,处理负数效率更高 n int32
sint64 64位整数,处理负数效率更高 n __int64
fixed32 32位无符号整数 4 unsigned int32
fixed64 64位无符号整数 8 unsigned __int64
sfixed32 32位整数,能以更高的效率处理负数 4 unsigned int32
sfixed64 64位整数 8 unsigned __int64
string 只能处理ascii字符 n std::string
bytes 用于处理多字节的语言字符,如中文 n std::string
enum 可以包含一个用户自定义的枚举类型uint32 n(uint32) enum
message 可以包含一个用户自定义的消息类型 n object 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

//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 
#include 
demo::data data;
data.set_x(20); //设置成员变量
qdebug()<

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

  • 提供has_方法判断变量值是否被设置;
  • 提供clear_方法清除设置的变量值 ;
//使用message
#include 
#include 
demo::data data;
data.set_x(20); //设置成员变量
qdebug()<

对于string类型

  • 提供了多种set_方法,其参数不同;
  • 提供了一个mutable_方法,返回变量值的可修改指针 ;
//使用message
#include 
#include 
demo::data data;
data.set_str(20); //设置成员变量
std::string* mutable_str(); //返回str变量值的可修改指针

对于repeated变量

  • _size方法:返回变量的长度;
  • 通过下脚标访问其中的数据成员组;
  • 通过下脚标返回其中的成员的mutable_的方法
  • _add方法:增加一个成员
//使用message
#include 
#include 
demo::data data;
for(int i=0; i<10; i  )
{
  
    data.d_add(i); //向d中添加成员
}
for(int i=0; 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 d;
			for(int i=0; id_size(); i  )
			{
  
				d.push_back(data_1->d(i));
			}
		}	
	}
	file.close();
}
网站地图