Protobuf介绍
参考资料:https://developers.google.cn/protocol-buffers/docs/cpptutorial
https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/
protobuf是一种用来序列化和回复结构体的工具。以前的使用二进制直接进行保存、发送的方式会出现一些内存布局、大小端的问题,难以扩展。xml的话,虽然对使用者友好,但是又有很大的性能损失,此外,设计xml结构比直接使用key-value要麻烦很多。
1、 编译protobuf
github下载源码 https://github.com/protocolbuffers/protobuf
./autogen.sh
如果出错的话 apt-get install这些:
autoconf
automake
ibtool
make
g++
unzip
./configure
./make
./make check
./make install(insatlled in /usr/local/lib)
2、 测试示例
新建hello.proto
syntax = “proto2”;
package lm;
message helloworld
{
required int32 id = 1; // ID
required string str = 2; // str
optional int32 opt = 3; //optional field
}
3、 编译proto文件
执行protoc address.proto --cpp_out=./
报错error while loading shared libraries: libprotobuf.so.20: cannot open shared object file: No such file or directory
因为库路径再/usr/local/lib
执行如下操作:
$sudo echo “/usr/local/lib” >> /etc/ld.so.conf
$ldconfig
再次编译,生成两个文件address.pb.h 和address.pb.cc生成一个类
4、 编写测试代码如下:
*
#include "hello.pb.h"
#include <iostream>
#include <fstream>
using namespace std;
int main(void)
{
lm::helloworld msg1;
msg1.set_id(101);
msg1.set_str("aaaaa");
// Write the new address book back to disk.
fstream output("./log", ios::out | ios::trunc | ios::binary);
if (!msg1.SerializeToOstream(&output)) {
cerr << "Failed to write msg." << endl;
return -1;
}
return 0;
}
编译g++ -g -Wall -std=c++11 helloworld.cpp hello.pb.cc -lpthread -lprotobuf
编译出错,报一堆未定义的引用的问题,是库连接出错
添加库路径g++ -g -Wall -std=c++11 helloworld.cpp hello.pb.cc -lpthread -L/usr/local/lib -lprotobuf
成功
5、 运行
发现一个很诡异的bug,编译的时候必须加上-lpthread而且-lpthread必须放在最后面,否则运行的时候报错如下:
[libprotobuf FATAL google/protobuf/generated_message_util.cc:812]
CHECK failed: (scc->visit_status.load(std::memory_order_relaxed)) ==
(SCCInfoBase::kRunning): terminate called after throwing an instance
of ‘google::protobuf::FatalException’ what(): CHECK failed:
(scc->visit_status.load(std::memory_order_relaxed)) ==
(SCCInfoBase::kRunning):
已放弃 (核心已转储)
6、 读取文件的测试代码如下:
#include “hello.pb.h”
#include
#include using namespace std;int main(void) { lm::helloworld msg1; fstream input("./log",
ios::in|ios::binary); if(!msg1.ParseFromIstream(&input)) {
cerr<<“Failed to parse hello”<<endl; return -1; } cout<< “id:
“<<msg1.id()<<”\n”<<msg1.str()<<endl; return 0; }
protobuf的语法
1、 syntax字段标识proto2和proto3语法
2、 标识符
3、 每个字段都有唯一的标识符,1-15内的标识符占用一个字节, 16到2047占用两个字节。最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]( (从FieldDescriptor::kFirstReservedNumber 到 FieldDescriptor::kLastReservedNumber))
4、 字段规则
singular 一个格式良好的消息应该有0个或者1个这种字段
repeated 可以重复任意次
5、 同过编译proto文件,每一个定义的消息生成了一个对应的C++类
6、 序列化以及从序列获取函数:
bool SerializeToArray(void* data, int sizet)const; //把消息对象序列化 bool
ParseFromArray(void* data, int size);
一个结合protobuf与libevent的示例
其中的serverthree以及clientthree 其中的配置文件net_config.xml内容如下:
<root>
<server>
<ip>192.168.6.128</ip>
<port>9989</port>
</server>
<client>
<ip>192.168.6.1</ip>
</client>
<root>