Protocol

Protocol Buffers介绍及例子

Posted by Liangjf on April 9, 2019

Protocol Buffers介绍及例子

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

protobuf协议是以一个 .proto 后缀的文件为基础,这个文件通过结构化数据类型的形式描述了一个数据信息。

protobuf的序列化底层原理:

you can see, each field in the message definition has a unique numbered tag. These tags are used to identify your fields in the message binary format, and should not be changed once your message type is in use. Note that tags with values in the range 1 through 15 take one byte to encode. Tags in the range 16 through 2047 take two bytes. So you should reserve the tags 1 through 15 for very frequently occurring message elements. Remember to leave some room for frequently occurring elements that might be added in the future.

翻译为: protobuf协议使用二进制格式表示Key字段;对value而言,不同的类型采用的编码方式也不同,如果是整型,采用二进制表示;如果是字符,会直接原样写入文件或者字符串(不编码)。

公式 field_number << 3)| wire_type,如果域号大于等于16,两个字节共16位,去掉移位的3位,去掉两个字节中第一个比特位, 总共16个比特位只有16-5==11个比特位用来表示Key,所以Key的域号要小于2^11== 2048。

T - L - V 的数据存储方式

  • Tag - Length - Value,标识 - 长度 - 字段值 存储方式
  • 以 标识 - 长度 - 字段值 表示单个数据,最终将所有数据拼接成一个 字节流,从而 实现 数据存储 的功能

正式因为采用PB自身框架代码和编译器完成和独特的编码方式,才会使protobuf的序列化紧凑,效率这么高。

最初的目录结构:

各文件内容:

helloworld.proto

cat helloworld.proto

syntax = "proto2";
package lm;
message helloworld
{
    required int32 id = 1;
    required string str = 2;
    optional int32 opt=3;
}

main.cpp

cat main.cpp

#include <iostream>
#include <fstream>
#include "protoc_dir/helloworld.pb.h"

using namespace std;
 
void ListMsg(const lm::helloworld & msg){
    cout << msg.id()<<endl;
    cout << msg.str()<<endl;
}
 
int main(void)
{
    lm::helloworld msg1;
    lm::helloworld msg2;
    msg1.set_id(101);     //设置id
    msg1.set_str("helloworld");    //设置字符串
    {
        fstream output("./tst.log", ios::out |ios::trunc |ios::binary);
        if (!msg1.SerializeToOstream(&output)){
            cerr << "failed to serialize msg1" <<endl;
            return -1;
        }
    }
    {
        fstream input("./tst.log",ios::in|ios::binary);
        if (!msg2.ParseFromIstream(&input)){
            cerr << "failed to parse" <<endl;
            return -1;
        }
        ListMsg(msg2);
    }
    return 0;
}

1.根据.proto文件生成.cc .h文件

protoc --cpp_out=./protoc_dir/ helloworld.proto

2.编译源代码

g++ -std=c++11 main.cpp protoc_dir/helloworld.pb.cc -lprotobuf -lpthread -o obj_run

注意: protobuf用到了多线程, 所以一定要加上 lpthread

3.运行可执行文件