Google Protocol Buffers浅析(protobuf-3.14.0 C++)
参考1:https://www.cnblogs.com/royenhome/archive/2010/10/29/1864860.html
参考2:https://www.cnblogs.com/royenhome/archive/2010/10/30/1865066.html
参考3:https://www.cnblogs.com/royenhome/archive/2010/10/30/1865153.html
参考4:https://www.cnblogs.com/royenhome/archive/2010/10/30/1865256.html
1. 安装
1.1. 说明文档
https://github.com/protocolbuffers/protobuf/blob/master/src/README.md
1.2. 安装步骤
下载c++源码包:
https://github.com/protocolbuffers/protobuf/releases/tag/v3.14.0 |
./configure make make check sudo make install sudo ldconfig # refresh shared library cache. |
Protocol buffers是一个用来序列化结构化数据的技术,支持多种语言诸如C++、Java以及Python语言,可以使用该技术来持久化数据或者序列化成网络传输的数据。相比较一些其他的XML技术而言,该技术的一个明显特点就是更加节省空间(以二进制流存储)、速度更快以及更加灵活。
通常,编写一个protocol buffers应用需要经历如下三步:
1) 定义消息格式文件,最好以proto作为后缀名
2) 使用Google提供的protocol buffers编译器来生成代码文件,一般为.h和.cc文件,主要是对消息格式以特定的语言方式描述
3) 使用protocol buffers库提供的API来编写应用程序
2.1. 定义Proto文件
protobuf-3.14.0/examples/addressbook.proto |
源码包中有addressbook.proto这样一个文件,可以拿它来做实验。
2.2. 编译
protoc --cpp_out=liuyanExample ./addressbook.proto |
将addressbook.proto编译在liuyanExample目录下,会生成addressbook.pb.h和addressbook.pb.cc文件
2.2.1. 生成的头文件
通过查看头文件,可以发现针对每个字段都会大致生成如下几种函数,以number为例,可以看出,对于每个字段会生成一堆的相关操作函数:
// string number = 1; void clear_number(); const std::string& number() const; void set_number(const std::string& value); void set_number(std::string&& value); void set_number(const char* value); void set_number(const char* value, size_t size); std::string* mutable_number(); std::string* release_number(); void set_allocated_number(std::string* number); private: const std::string& _internal_number() const; void _internal_set_number(const std::string& value); std::string* _internal_mutable_number(); public: |
而对于字段修饰符为repeated的字段生成的函数,则稍微有一些不同,如phone字段,可以看出,set函数变成了add函数,编译器会为其产生如下的代码:
// repeated .tutorial.Person.PhoneNumber phones = 4; int phones_size() const; private: int _internal_phones_size() const; public: void clear_phones(); ::tutorial::Person_PhoneNumber* mutable_phones(int index); ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phones(); private: const ::tutorial::Person_PhoneNumber& _internal_phones(int index) const; ::tutorial::Person_PhoneNumber* _internal_add_phones(); public: const ::tutorial::Person_PhoneNumber& phones(int index) const; ::tutorial::Person_PhoneNumber* add_phones(); const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phones() const; |
2.3. 应用:序列化与反序列化
参考Makefile,执行make
g++ test.cpp addressbook.pb.cc -std=c++11 -lprotoc -lprotobuf -lpthread -g |
序列化后的数据大概是下面这个样子:
serialize.cpp
1 #include <ctime> 2 #include <fstream> 3 #include <iostream> 4 #include <string> 5 #include <google/protobuf/util/time_util.h> 6 #include <sys/types.h> 7 #include <sys/stat.h> 8 #include <fcntl.h> 9 #include <unistd.h> 10 11 //#include <google/protobuf/io/zero_copy_stream_impl.h> 12 //#include <google/protobuf/io/zero_copy_stream.h> 13 //#include <google/protobuf/io/coded_stream.h> 14 #include "addressbook.pb.h" 15 using namespace std; 16 using namespace::google::protobuf::io; 17 //1ok using namespace::tutorial; 18 using namespace tutorial; 19 20 21 //地址簿数据定义 22 //1ok tutorial::AddressBook addressBook; 23 AddressBook addressBook; 24 25 #if 1 26 void testAddData(void) 27 { 28 //第一个联系人的数据定义与初始化 29 Person *personMe = addressBook.add_people(); 30 personMe->set_id(1); 31 personMe->set_name("liuliu"); 32 personMe->set_email("liuliu@163.com"); 33 //personMe->set_unsure("19bf173a0e87ab"); 34 35 #if 1 36 //第二个联系人的数据定义与初始化 37 Person *personHim = addressBook.add_people(); 38 personHim->set_id(2); 39 personHim->set_name("hhh"); 40 personHim->set_email("hhh@him.com"); 41 //personHim->set_unsure("19bf173a0e87ab"); 42 43 //personMe的手机号码数据定义与初始化 44 Person_PhoneNumber *phoneNumberMobile = personMe->add_phones(); 45 phoneNumberMobile->set_number("18210002000"); 46 phoneNumberMobile->set_type(Person_PhoneType_MOBILE); 47 48 //personMe的座机号码数据定义与初始化 49 Person_PhoneNumber *phoneNumberHome = personMe->add_phones(); 50 phoneNumberHome->set_number("01088889999"); 51 phoneNumberHome->set_type(Person_PhoneType_HOME); 52 53 //personHim的一个号码数据定义与初始化 54 Person_PhoneNumber *phoneNumberHim = personHim->add_phones(); 55 phoneNumberHim->set_number("15988881111"); 56 phoneNumberHim->set_type(Person_PhoneType_HOME); 57 58 #endif 59 } 60 61 void testSerializePartialToOstream(void) 62 { 63 //方法一: 使用SerializePartialToOstream来序列化,注意ios::binary以二进制流写入文件 64 int fd = open("addressbook.data", O_WRONLY |O_CREAT, S_IREAD| S_IWRITE); 65 close(fd); 66 FILE *g_AddressBook = fopen("addressbook.data","wb,ccs = UNICODE"); 67 fclose(g_AddressBook); 68 g_AddressBook = NULL; 69 70 fstream fserial("addressbook.data",ios::out | ios::trunc | ios::binary); 71 if (!addressBook.SerializePartialToOstream(&fserial)) 72 { 73 cerr<<"Failed to serial address book data!\n"; 74 return; 75 } 76 cout<<"Serial address book data successfully!\n"; 77 fserial.close(); 78 fserial.clear(); 79 } 80 81 void testSerializePartialToString(void) 82 { 83 FILE *g_AddressBook = fopen("addressbook.data2_toString","wb,ccs = UNICODE"); 84 if( NULL == g_AddressBook ) 85 { 86 cerr<<"Create addressbook.data failed!\n"; 87 return ; 88 } 89 90 string serialStream = ""; 91 if( !addressBook.SerializePartialToString(&serialStream) ) 92 { 93 cerr<<"Failed to serial addressbook data!\n"; 94 return; 95 } 96 97 fwrite( serialStream.c_str(),sizeof(char),addressBook.ByteSize(),g_AddressBook); 98 cout<<"serial address successfully!\n"; 99 if( g_AddressBook ) 100 { 101 fclose(g_AddressBook); 102 g_AddressBook = NULL; 103 } 104 } 105 106 void testSerializeToCodedStream(void) 107 { 108 #define MAX_SIZE (1024) 109 int fd = open("addressbook.data3_toCodedStream", O_WRONLY |O_CREAT, S_IREAD| S_IWRITE); 110 if( -1 == fd ) 111 { 112 cerr<<"Create addressbook.data failed!\n"; 113 return ; 114 } 115 char tmpArr[MAX_SIZE]; 116 memset(tmpArr,0,sizeof(tmpArr)); 117 ZeroCopyOutputStream *raw_output = new ArrayOutputStream(tmpArr,addressBook.ByteSize()+1); 118 CodedOutputStream* coded_output = new CodedOutputStream(raw_output); 119 if( !addressBook.SerializeToCodedStream( coded_output )) 120 { 121 cerr<<"Fail to serial addressbook data!\n"; 122 return; 123 } 124 write(fd,tmpArr,addressBook.ByteSize()+1); 125 cout<<"serial address successfully!\n"; 126 delete coded_output; 127 delete raw_output; 128 close(fd); 129 } 130 #endif 131 132 int main(int argc, char *argv[]) 133 { 134 135 testAddData(); 136 testSerializePartialToOstream(); 137 testSerializePartialToString(); 138 testSerializeToCodedStream(); 139 140 return 0; 141 }
serialize_reverse.cpp
1 #include <ctime> 2 #include <fstream> 3 #include <iostream> 4 #include <string> 5 #include <google/protobuf/util/time_util.h> 6 #include <sys/types.h> 7 #include <sys/stat.h> 8 #include <fcntl.h> 9 #include <unistd.h> 10 11 #include "addressbook.pb.h" 12 using namespace std; 13 using namespace::google::protobuf::io; 14 using namespace tutorial; 15 16 17 char *buffer = NULL; 18 int lfilesize = 0; 19 20 21 unsigned long get_file_size(const char *filename) 22 { 23 struct stat buf; 24 if(stat(filename, &buf)<0) 25 { 26 return 0; 27 } 28 return (unsigned long)buf.st_size; 29 } 30 31 void testLoadData() 32 { 33 const char *fileName = "./addressbook.data"; 34 FILE *g_AddressBook = fopen(fileName, "rb,ccs=UNICODE"); 35 cout<<"testLoadData begin!"<<endl; 36 if( NULL == g_AddressBook ) 37 { 38 cerr<<"Open addressbook.data failed!\n"<<endl; 39 return ; 40 } 41 42 cout<<"testLoadData 1!"<<endl; 43 44 fseek( g_AddressBook,0-1,SEEK_END); 45 lfilesize = 1 + ftell( g_AddressBook ); 46 fseek( g_AddressBook ,0,SEEK_SET ); 47 48 cout<<"testLoadData 2 lfilesize="<< lfilesize <<endl; 49 fseek( g_AddressBook ,0,SEEK_SET ); 50 51 buffer =new char[lfilesize+1]; 52 if( NULL == buffer ) 53 { 54 cerr<<"malloc memory error!\n"; 55 return; 56 } 57 58 cout<<"testLoadData 3!, tell="<<ftell( g_AddressBook )<<endl; 59 60 memset(buffer,'\0',sizeof(buffer)); 61 int rt =fread( buffer,sizeof(char),lfilesize,g_AddressBook); 62 cout<<"fread rt="<<rt<<",lfilesize="<<lfilesize<<endl; 63 if( g_AddressBook ) 64 { 65 fclose(g_AddressBook); 66 g_AddressBook = NULL; 67 } 68 69 cout<<"testLoadData over!"<<endl; 70 71 } 72 73 void testSerialize_reverse() 74 { 75 AddressBook addressBook; 76 //addressBook.par 77 addressBook.clear_people(); 78 79 cerr<<"Deserial from addressbook.data size="<<lfilesize<<endl; 80 //cerr<<"Deserial from addressbook.data buffer="<<buffer<<endl; 81 if( !addressBook.ParseFromArray(buffer,lfilesize) ) 82 { 83 cerr<<"Deserial from addressbook.data failed!\n"; 84 return; 85 } 86 87 int personSize = addressBook.people_size(); 88 cout<<"personSize="<<personSize<<endl; 89 90 for( int i=0 ;i<personSize; i++ ) 91 { 92 Person p = addressBook.people( i ); 93 cout<<"Person "<<i+1<<":\nid\t"<<p.id()<<"\nname:\t"<<p.name()<<"\n"; 94 int phoneSize = p.phones_size(); 95 for( int j=0;j<phoneSize;j++ ) 96 { 97 Person_PhoneNumber phone = p.phones(j); 98 cout<<"Phone "<<j+1<<":\nType:\t"; 99 switch( phone.type()) 100 { 101 case Person_PhoneType_MOBILE: 102 cout<<"Mobile\t\tPhone Number:\t"<<phone.number()<<endl; 103 break; 104 case Person_PhoneType_HOME: 105 cout<<"Home\t\tPhone Number:\t"<<phone.number()<<endl; 106 break; 107 case Person_PhoneType_WORK: 108 cout<<"Work\t\tPhone Number:\t"<<phone.number()<<endl; 109 break; 110 default: 111 cout<<"Unkown\n"; 112 break; 113 } 114 } 115 cout<<endl; 116 } 117 } 118 119 120 int main(int argc, char *argv[]) 121 { 122 testLoadData(); 123 testSerialize_reverse(); 124 125 return 0; 126 }
Makefile
all: g++ serialize_reverse.cpp addressbook.pb.cc -std=c++11 -lprotoc -lprotobuf -lpthread -o serial_re g++ serialize.cpp addressbook.pb.cc -std=c++11 -lprotoc -lprotobuf -lpthread -o serial clean: rm serial serial_re -f rm -f addressbook.data*