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*

 

posted @ 2021-01-21 19:08  LiuYanYGZ  阅读(543)  评论(0编辑  收藏  举报