Gstyle-Protocol Buffers(protobuf)

| 2011年1月7日

部门内部在大行其道Gstyle—gtest、glog、protobuf。。。。。。所以我也就先学习学习,以备后面使用的时候在手慌脚乱的,上个月对gtest的使用做了一些学习,基本在日常的开发中已经开始慢慢使用了,而且我自己也根据网友的一个项目lcut分了一个分支xcut–主要是增加了一些我想要的特点。

这几天又学习了protobuf,是google的一个广泛的数据包协议,有了这个就可以把这边定义的包发送到对方,对方就可以很简单的解析出来,就类似于xml,json等协议一样,据网上说,该协议效率很高,空间占有量又很小,是一个比xml或是json要好很多的协议,但是一点就是可读性比较差,看不懂数据是怎么排列和各个字段的意思。

Protobuf的安装就不说了,主要说说使用吧,我从其源码的example中抽取了一个例子来说明一下:首先是要有一个xxx..proto的文件的,这个文件中定义你要使用的协议:这里我修改后的例子如下:(以cpp为例哦)

	//xtest.proto
	package xtest;
	message Person {
	  required string name = 1;
	  required int32 id = 2;    
	  optional string email = 3;
	  message PhoneNumber {
	    required string number = 1;
	  }
	  required PhoneNumber phone = 4;
	}
	message AddressBook {
	  repeated Person person = 1;
	}
Proto注释的添加和cpp一样,用双斜杠即可。使用这样的语句将其转换为cpp文件和相应的.h文件:(前提是已经安装了Protobuf)
	#ls
	Makefile  xtest.cc  xtest.proto
	#protoc --cpp_out=. xtest.proto
	#ls
	Makefile  xtest.cc  xtest.pb.cc  xtest.pb.h  xtest.proto
	#

package xtest:表示xtest是一个命名空间,在转换后xtest.pb.cc xtest.pb.h 的文件中展现:

namespace xtest {。。。。。。

message Person:表示Person是一个类,里面的内容是其成员。在转换后的文件中以class展现。message AddressBook 也是一样的,表示Person中的一个类。可以在一个文件中添加多个message,在编译生成cpp代码后,将对应多个类。

类中成员也有其类型标签:

  • A.required 一个结构良好的message必须有一个这样的字段。
  • B.optional 结构良好的message有零个或者一个这样的字段。
  • C.repeated 这个字段可以重复任意多次(包括零次)。

说明:在repeated后面加上[packed = true]可以取得更高效的编码。如:repeated int32 samples = 4 [package=true] required string name = 1:其中=1是给指定的一个标签,一个类中的标签是唯一的,在使用范围上是1~229-1,当然19000~19999是不可以使用的,因为protocol buffer内部使用。其中1~15保留为经常使用的消息元素。

对于每个成员变量,在生成xtest.pb.cc xtest.pb.h 文件后都会有相应的方法。 比如对上面的name有一下一些方法:

 inline bool has_name() const;
 inline void clear_name();
 inline const ::std::string& name() const;
 inline void set_name(const ::std::string& value); 
inline void set_name(const char* value); 
inline ::std::string* mutable_name();

不同类型的字段,其方法稍微有些不通,但是大多数都一样的。其它具体可以在生成这些文件后稍微搜索一下这些文件就可以了解了。

在具体的使用上如下:

#include "xtest.pb.h"
int main(int argc, char* argv[]) { 
 GOOGLE_PROTOBUF_VERIFY_VERSION;//对protobuf使用的环境变量的初始化和对比
..................................  
google::protobuf::ShutdownProtobufLibrary();//清场,protobuf使用的零时数据,环境变量等  
return 0;
}

一下是一个对这个结构添加和读取显示的例子:(由protobuf源码中的例子修改而来)

#include <iostream>
#include <fstream>
#include <string>
#include "xtest.pb.h"
using namespace std;

void PromptForAddress(xtest::Person* person) {
    cout << "Enter person ID number : ";
    int id;
    cin >> id;
    person->set_id(id);
    cin.ignore(256, '\n');

    cout << "Enter name: ";
    getline(cin, *person->mutable_name());

    cout << "Enter email address (blank for none): ";
    string email;
    getline(cin, email);
    if (!email.empty()) {
      person->set_email(email);
    }

    cout << "Enter a phone number : ";
    string number;
    getline(cin, number);

    xtest::Person::PhoneNumber* phone_number = person->mutable_phone();
    phone_number->set_number(number);

}

void ListPeople(const xtest::AddressBook& address_book) {
  for (int i = 0; i < address_book.person_size(); i++) {
    const xtest::Person& person = address_book.person(i);

    cout << "Person ID: " << person.id() << endl;
    cout << "  Name: " << person.name() << endl;
    if (person.has_email()) {
      cout << "  E-mail address: " << person.email() << endl;
    }
        if (person.has_phone()) {
      const xtest::Person::PhoneNumber& phone_number = person.phone();
      cout << "Phone #: " << phone_number.number() << endl;
        }
  }
}

int main(int argc, char* argv[]) {
  GOOGLE_PROTOBUF_VERIFY_VERSION;
  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    return -1;
  }
  xtest::AddressBook address_book;
  {
    fstream input(argv[1], ios::in | ios::binary);
    if (!input) {
      cout << argv[1] << ": File not found.  Creating a new file." << endl;
    } else if (!address_book.ParseFromIstream(&input)) {
      cerr << "Failed to parse address book." << endl;
      return -1;
    }
  }
  while (true) {
    cout << "Input 0 to finish Other continue: ";
    int id;
    cin >> id;
    if (id == 0) {
      break;
    }
    PromptForAddress(address_book.add_person());
    {
      fstream output(argv[1], ios::out | ios::trunc | ios::binary);
      if (!address_book.SerializeToOstream(&output)) {
        cerr << "Failed to write address book." << endl;
        return -1;
      }
    }
  }
  ListPeople(address_book);

  google::protobuf::ShutdownProtobufLibrary();
  return 0;
}
看完本文有收获?请分享给更多人

关注「黑光技术」,关注大数据+微服务