protobuf入門

jopen 9年前發布 | 142K 次閱讀 protobuf

protobuf全稱Protocol Buffers,是google推出的一種高效,快捷的數據交換格式,和XML,Thrift一樣,都是一種數據交換協議(當然thrift還提供rpc的功能)。protobuf相對與xml結構化的文本數據格式,它是一種二進制的數據格式,具有更高的傳輸,打包和解包效率,這也是為什么protobuf很受歡迎的原因。

protobuf通過自己的編譯器,對協議文件進行編譯,生成對應語言的代碼,方便的進行數據的打包和解包。目前,google 提供了三種語言的實現:java、c++ 和python,每一種實現都包含了相應語言的編譯器以及庫文件。

下面介紹protobuf的語法,protobuf的IDL都是保存為*.proto的文件中,proto文件中數據類型可以分為兩大類復合數據類型標準數據類型復合數據類型包括:枚舉message類型標準數據類型包含:整型,浮點,字符串等,后面會詳細介紹。

  • message

最常用的數據格式就是message,例如一個訂單數據可以用message表示如下:

message Order
{
    required uint64 uid = 1;
    required float cost = 2;
    optional string tag = 3;
}
它經過protobuf編譯成c++代碼,會生成對應的XXX.pb.h和XXX.pb.cc。message會對應生成一個class,里面存放對應的data members,處理這些數據的函數,以及對應的打包和解包函數。
class Order : public ::google::protobuf::Message {
 public:
  ...
  // accessors -------------------------------------------------------
  ...

  ::google::protobuf::uint64 uid_;
  ::std::string* tag_;
  float cost_;
};
message數據格式中需要知道的:

1.每個字段末尾賦值的tag:該tag是用來標記該字段在序列化后的二進制數據中所在的field,每個字段的tag在message內部都是獨一無二的。也不能進行改變,否則數據就不能正確的解包。

2.數據類型前面的修飾詞:

  • required: 必須賦值,不能為空,否則該條message會被認為是“uninitialized”。build一個“uninitialized” message會拋出一個RuntimeException異常,解析一條“uninitialized” message會拋出一條IOException異常。除此之外,“required”字段跟“optional”字段并無差別。
  • optional:字段可以賦值,也可以不賦值。假如沒有賦值的話,會被賦上默認值。
  • repeated: 該字段可以重復任意次數,包括0次。重復數據的順序將會保存在protocol buffer中,將這個字段想象成一個可以自動設置size的數組就可以了。
  • 枚舉

枚舉和c++,java中的枚舉類型是一個含義:

enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;    
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
}

執行 protoc --cpp_out=.  enum_test.proto,會生成以下c++代碼

enum Corpus {
  UNIVERSAL = 0,
  WEB = 1,
  IMAGES = 2,
  LOCAL = 3,
  NEWS = 4,
  PRODUCTS = 5,
  VIDEO = 6
};

  • 基本數據類型

protobuf支持的基本數據類型如下圖:

  • message詳解:

message數據格式在c++中被protobuf自動編譯包含一下內容:

//xxx.proto
message Order
{
    required uint64 uid = 1;
    required float cost = 2;
    optional string tag = 3;
}

//xxx.pb.h
<pre name="code" class="cpp">class Order : public ::google::protobuf::Message {
 public:
  ...
  // accessors -------------------------------------------------------

  // required uint64 uid = 1;
  inline bool has_uid() const;
  inline void clear_uid();
  static const int kUidFieldNumber = 1;
  inline ::google::protobuf::uint64 uid() const;
  inline void set_uid(::google::protobuf::uint64 value);

  // required float cost = 2;
  inline bool has_cost() const;
  inline void clear_cost();
  static const int kCostFieldNumber = 2;
  inline float cost() const;
  inline void set_cost(float value);

  // optional string tag = 3;
  inline bool has_tag() const;
  inline void clear_tag();
  static const int kTagFieldNumber = 3;
  inline const ::std::string& tag() const;
  inline void set_tag(const ::std::string& value);
  inline void set_tag(const char* value);
  inline void set_tag(const char* value, size_t size);
  inline ::std::string* mutable_tag();
  inline ::std::string* release_tag();
  inline void set_allocated_tag(::std::string* tag);

  // @@protoc_insertion_point(class_scope:Order)
 private:
  inline void set_has_uid();
  inline void clear_has_uid();
  inline void set_has_cost();
  inline void clear_has_cost();
  inline void set_has_tag();
  inline void clear_has_tag();

  ::google::protobuf::uint32 _has_bits_[1];

  ::google::protobuf::uint64 uid_;
  ::std::string* tag_;
  float cost_;
};
 對于每一個message的data member,protobuf會自動生成相關的處理函數,對于每一個字段主要的處理函數有:has_uid(), clear_uid(), uid(), set_uid(),它們分別用于判斷該字段是否被設置,清除該字段設置記錄,獲得該字段,設置該字段。對于示例中的uid字段,對應函數的實現如下:

//xxx.pb.h

// required uint64 uid = 1;
inline bool Order::has_uid() const {
  return (_has_bits_[0] & 0x00000001u) != 0;
}
inline void Order::set_has_uid() {
  _has_bits_[0] |= 0x00000001u;
}
inline void Order::clear_has_uid() {
  _has_bits_[0] &= ~0x00000001u;
}
inline void Order::clear_uid() {
  uid_ = GOOGLE_ULONGLONG(0);
  clear_has_uid();
}
inline ::google::protobuf::uint64 Order::uid() const {
  // @@protoc_insertion_point(field_get:Order.uid)
  return uid_;
}
inline void Order::set_uid(::google::protobuf::uint64 value) {
  set_has_uid();
  uid_ = value;
  // @@protoc_insertion_point(field_set:Order.uid)
}
由實現代碼可知,代碼是通過_has_bits_來標記字段是否已經被設置,_has_bits_的定義如下:
::google::protobuf::uint32 _has_bits_[1];

 通過_has_bits_的位來表達各個字段是否被設置。分別通過0x01, 0x02, 0x04...來分別標記第1,2,3,,,各個field是否已經被設置。

對于protobuf將協議數據序列化為二進制數據的接口有如下:

// Serialization ---------------------------------------------------
  // Methods for serializing in protocol buffer format.  Most of these
  // are just simple wrappers around ByteSize() and SerializeWithCachedSizes().

  // Write a protocol buffer of this message to the given output.  Returns
  // false on a write error.  If the message is missing required fields,
  // this may GOOGLE_CHECK-fail.
  bool SerializeToCodedStream(io::CodedOutputStream* output) const;
  // Like SerializeToCodedStream(), but allows missing required fields.
  bool SerializePartialToCodedStream(io::CodedOutputStream* output) const;

  // Write the message to the given zero-copy output stream.  All required
  // fields must be set.
  bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
  bool SerializePartialToZeroCopyStream(io::ZeroCopyOutputStream* output) const;

  // Serialize the message and store it in the given string.  All required
  // fields must be set.
  bool SerializeToString(string* output) const;
  bool SerializePartialToString(string* output) const;

  // Serialize the message and store it in the given byte array.  All required
  // fields must be set.
  bool SerializeToArray(void* data, int size) const;
  bool SerializePartialToArray(void* data, int size) const;

  string SerializeAsString() const;
  string SerializePartialAsString() const;

  // Like SerializeToString(), but appends to the data to the string's existing
  // contents.  All required fields must be set.
  bool AppendToString(string* output) const;
  bool AppendPartialToString(string* output) const;

  // Serialize the message and write it to the given file descriptor.  All
  // required fields must be set.
  bool SerializeToFileDescriptor(int file_descriptor) const;
  bool SerializePartialToFileDescriptor(int file_descriptor) const;

  // Serialize the message and write it to the given C++ ostream.  All
  // required fields must be set.
  bool SerializeToOstream(ostream* output) const;
  bool SerializePartialToOstream(ostream* output) const;

 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!