1、什么是Protocol buffer
Protocol buffer是由Geogle开发的通用通信协议,一种语言无关、平台无关、扩展性好的用于通信协议、数据存储的结构化数据序列化方法。
2、protocol buffer的使用及数据类型介绍
protocol buffer(下面介绍时,统称为p_b)在使用上,有些像c语言的结构体,声明结构体名称及结构体内部的相关变量。 下面通过一个示例,来了解p_b的相关结构体定义及其类型
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
repeated PhoneNumber phone = 4;
message
定义一个消息数据的名称,相当于struct中的结构体名称
required
我理解这属于一个属性字段,设置为required的字段,必须要被用户使用
optional
同上,一个属性字段,设置该属性字段后,读取message数据的一方可以选择读取该字段,也可以不对,并不做强制要求。
enum
枚举,其最用与c语言中的枚举时相同的用法
repeated
可重复(变长)字段,设置了该属性的字段,我们可以将其理解为定义的一个数组
上面主要为一些portocol的相关属性字段设置,下面的表格则是关于变量类型定义时的数据类型字段
protocol类型 | C++类型 |
---|---|
double | double |
float | float |
bool | bool |
string | string |
bytes | string |
其中还有四个类型与c语言的有区别
protocol类型 | C++类型 | 补充 |
---|---|---|
int32 | int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. |
int64 | int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. |
uint32 | uint32 | Uses variable-length encoding. |
uint64 | uint64 | Uses variable-length encoding. |
sint32 | int32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. |
sint64 | int64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. |
fixed32 | uint32 | Always four bytes. More efficient than uint32 if values are often greater than 228. |
fixed64 | uint64 | Always eight bytes. More efficient than uint64 if values are often greater than 256. |
sfixed32 | int32 | Always four bytes. |
sfixed64 | int64 | Always eight bytes. |
3、示例
下面利用protocol buffer生成一个消息类,并在程序中使用
a. 编写一个.prob文件,命名为lm.hello.prob
package lm;
message helloworld
{
required int32 id = 1; // ID
required string str = 2; // str
optional int32 opt = 3; //optional field
}
将其编译输出为cpp类型文件
protoc lm.hello.proto --cpp_out=./
b. 编写一个cpp程序来使用这个message类
这里会写一个write基于message写入文件,再写一个read基于message来读取文件
- write.cpp
#include<iostream>
#include<fstream>
#include"lm.hello.pb.h"
int main()
{
lm::helloworld msg1;
msg1.set_id(101);
msg1.set_str("hello");
msg1.add_nums(1);
msg1.add_nums(2);
std::fstream output("./log",std::ios::out|std::ios::trunc |std::ios::binary);
if(!msg1.SerializeToOstream(&output)){
std::cout<<"Failed to write msg"<<std::endl;
return -1;
}
return 0;
}
- read.cpp
#include<iostream>
#include<fstream>
#include"lm.hello.pb.h"
void ListMsg(const lm::helloworld &msg){
std::cout<<msg.id()<<std::endl;
std::cout<<msg.str()<<std::endl;
for(int i =0;i<msg.nums_size();i++)
std::cout<<msg.nums(i)<<std::endl;
}
int main(){
lm::helloworld msg1;
std::fstream input("./log",std::ios::in|std::ios::binary);
if(!msg1.ParseFromIstream(&input)){
std::cout<<"Failed to parse address book"<<std::endl;
return -1;
}
ListMsg(msg1);
}
c. 进行编译
g++ write.cpp lm.hello.pb.cc lm.hello.pb.h -o write -lprotobuf
g++ read.cpp lm.hello.pb.cc lm.hello.pb.h -o read -lprotobuf
d. 执行及结果显示
root@fafZ:~/WorkCode/Protocol/code# clear
root@fafZ:~/WorkCode/Protocol/code# ./write
root@fafZ:~/WorkCode/Protocol/code# ./read
101
hello
1
2
4、Protocol buffer的相关api介绍
先通过上面的例子来对Protocol buffer进行介绍
下面在介绍相关接口,都是基于message变量已设置
set_字段名()
//写入
messgae.set_id(5); //给message中的id字段赋值
//读取
message.id(); //直接从message中读取数据
add_字段名()
//写入
message.add_nums(1);
message.add_nums(2);
//读取
for(int i=0;i<message.nums_size();i++){
std::cout<<"message.num"<<i<<":"<<message.nums(i);
}
add主要是针对repeated属性的字段进行数值的添加,同时对repeated数据的访问也是需要对应下标去访问
SerializeToOstream(ostream* output) const 和 ParseFromIstream(istream* input)
std::fstream output("./log",std::ios::out|std::ios::trunc |std::ios::binary);
msg1.SerializeToOstream(&output);
std::fstream input("./log",std::ios::in|std::ios::binary);
msg1.ParseFromIstream(&input);
该接口会将msg1中设置的数据都进行序列化操作,保存在对应的文件流中
SerializeToString(string* output) const 和 ParseFromString(const string& data)
std::string str;
obj.SerializeToString(&str);
obj2.ParseFromString(str);
与上面接口相似,只是该接口会把message中的数据存放在字符串类型中,再反序列化时,需要用到对应的string对象。
其他
当我们在程序中要对相关字段进行操作时,一般可以打开生成的.h文件,里面可以查看对应字段.proto提供给我们的接口。