精华内容
下载资源
问答
  • Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。Protocol ...

    Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。

    编译源码包

    从github:https://github.com/protocolbuffers/protobuf 下载源代码,此处我下载的是2.5.0版。
    在这里插入图片描述
    解压源码包,解压后文件如下图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    执行配置编译:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    源码编译后创建的文件:
    在这里插入图片描述
    可以看到protobuf-install目录下有bin、include和lib目录:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    编译测试工程

    把include目录下的文件都按照该目录结构和lib/libprotobuf.a复制到所测试的目录中去
    在这里插入图片描述
    准备用于演示的结构化数据是Content,它包含两个基本数据:id是一个整数类型int32的数据;str是一个字符串string;opt是一个可选的成员,即消息中可以不包含该成员。
    Mymessage.proto代码:

      1 package Im;
      2 message Content
      3 {
      4         required int32  id = 1;
      5         required string str = 2;
      6         optional int32  opt = 3;
      7 }
    

    执行安装目录下bin目录中的protoc程序,将写好的proto 文件用Protobuf 编译器将该文件编译成目标语言。命令生成Mymessage.pb.h和Mymessage.pb.cc文件,如下图所示:
    在这里插入图片描述
    生成的文件
    在这里插入图片描述

    现在要将数据存入磁盘,该结构化数据由Im::Content类的对象表示,它提供一系列的get/set函数用来修改和读取结构化数据中的数据成员。当需要将该结构化数据保存到磁盘上时,类Im::Content已经提供相应的方法来把一个复杂的数据变成一个字节序列,可以将这个字节写入磁盘。
    Writer.cpp代码:

      1 #include <iostream>
      2 #include <fstream>
      3 #include "Mymessage.pb.h"
      4 
      5 int main()
      6 {
      7         Im::Content msg1;
      8         msg1.set_id(10);
      9         msg1.set_str("hello world");
     10         std::fstream output("./log", std::ios::out|std::ios::trunc|std::ios::binary);
     11         if(!msg1.SerializeToOstream(&output))
     12         {
     13                 std::cerr << "Failed to write msg." << std::endl;
     14                 return -1;
     15         }
     16         return 0;
     17 }
    

    先生成Mymessage.pb.o文件,然后再编译Writer.cpp文件。
    在这里插入图片描述
    执行Writer文件,生成log文件。

    对于Reader,只需从log文件中读取,反序列化后就能获得结构化的数据。利用Im::Cotent对象的ParseFromIstream方法从一个fstream流中读取信息并反序列化,此后,ListMsg 中采用 get 方法读取消息的内部信息,并进行打印输出操作。

      1 #include <iostream>
      2 #include <fstream>
      3 #include "Mymessage.pb.h"
      4 
      5 void ListMsg(const Im::Content& msg)
      6 {
      7         std::cout << msg.id() << std::endl;
      8         std::cout << msg.str() << std::endl;
      9 }
     10 int main()
     11 {
     12         Im::Content msg1;
     13         std::fstream input("./log", std::ios::in|std::ios::binary);
     14         if(!msg1.ParseFromIstream(&input))
     15         {
     16                 std::cerr << "Failed to parse address book." << std::endl;
     17                 return -1;
     18         }
     19         ListMsg(msg1);
     20         return 0;
     21 }
    

    在这里插入图片描述

    执行效果:
    在这里插入图片描述

    Protobuf 的优点

    • Protobuf有如XML,不过它更小、更快、也更简单。你可以定义自己的数据结构,然后使用代码生成器生成的代码来读写这个数据结构。你甚至可以在无需重新部署程序的情况下更新数据结构。只需使用Protobuf对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。
    • 它有一个非常棒的特性,即“向后”兼容性好,人们不必破坏已部署的、依靠“老”数据格式的程序就可以对数据结构进行升级。这样您的程序就可以不必担心因为消息结构的改变而造成的大规模的代码重构或者迁移的问题。因为添加新的消息中的 field 并不会引起已经发布的程序的任何改变。
    • Protobuf 语义更清晰,无需类似 XML 解析器的东西(因为 Protobuf 编译器会将 .proto 文件编译生成对应的数据访问类以对 Protobuf 数据进行序列化、反序列化操作)。
    • 使用 Protobuf 无需学习复杂的文档对象模型,Protobuf 的编程模式比较友好,简单易学,同时它拥有良好的文档和示例,对于喜欢简单事物的人们而言,Protobuf 比其他的技术更加有吸引力。

    Protobuf 的不足

    Protbuf 与 XML 相比也有不足之处。它功能简单,无法用来表示复杂的概念。XML 已经成为多种行业标准的编写工具,Protobuf 只是 Google 公司内部使用的工具,在通用性上还差很多。由于文本并不适合用来描述数据结构,所以 Protobuf 也不适合用来对基于文本的标记文档(如 HTML)建模。另外,由于 XML 具有某种程度上的自解释性,它可以被人直接读取编辑,在这一点上 Protobuf 不行,它以二进制的方式存储,除非你有 .proto 定义,否则你没法直接读出 Protobuf 的任何内容

    高级应用话题

    嵌套 Message

    嵌套Message的例子:

    message Person {
    	required string name = 1;
    	required int32  id = 2;
    	optional string email = 3;
    
    	enum PhoneType{
    		MOBILE = 0;
    		HOME = 1;
    		WORK = 2;
    	}
    
    	message PhoneNumber{
    		required string number = 1;
    		optional PhoneType type = 2 [default = HOME];
    	}
    	repeated PhoneNumber phone = 4;
    }
    

    在 Message Person 中,定义了嵌套消息 PhoneNumber,并用来定义 Person 消息中的 phone 域。

    Import Message

    在一个 .proto 文件中,还可以用 Import 关键字引入在其他 .proto 文件中定义的消息,这可以称做 Import Message,或者 Dependency Message。

    import common.header;
    message youMsg{
    	required common.info_header header = 1;
    	required string youPrivateData = 2;
    }
    

    其中 ,common.info_header定义在common.header包内。

    Import Message 的用处主要在于提供了方便的代码管理机制,类似 C 语言中的头文件。您可以将一些公用的 Message 定义在一个 package 中,然后在别的 .proto 文件中引入该 package,进而使用其中的消息定义。

    动态编译

    一般情况下,使用 Protobuf 的人们都会先写好 .proto 文件,再用 Protobuf 编译器生成目标语言所需要的源代码文件。将这些生成的代码和应用程序一起编译。
    可是在某且情况下,人们无法预先知道 .proto 文件,他们需要动态处理一些未知的 .proto 文件。比如一个通用的消息转发中间件,它不可能预知需要处理怎样的消息。这需要动态编译 .proto 文件,并使用其中的 Message。Protobuf 提供了 google::protobuf::compiler 包来完成动态编译的功能。主要的类叫做 importer,定义在 importer.h 中。使用 Importer 非常简单,下图展示了与 Import 和其它几个重要的类的关系。
    在这里插入图片描述
    Import 类对象中包含三个主要的对象,分别为处理错误的 MultiFileErrorCollector 类,定义 .proto 文件源目录的 SourceTree 类。

    对于给定的 proto 文件,比如 lm.helloworld.proto,在程序中动态编译它只需要很少的一些代码

    google::protobuf::compiler::MultiFileErrorCollector errorCollector;
    google::protobuf::compiler::DiskSourceTree sourceTree;  
    google::protobuf::compiler::Importer importer(&sourceTree, &errorCollector);
    sourceTree.MapPath("", protosrc);  
    importer.import(“lm.helloworld.proto”);
    

    首先构造一个 importer 对象。构造函数需要两个入口参数,一个是 source Tree 对象,该对象指定了存放 .proto 文件的源目录。第二个参数是一个 error collector 对象,该对象有一个 AddError 方法,用来处理解析 .proto 文件时遇到的语法错误。之后,需要动态编译一个 .proto 文件时,只需调用 importer 对象的 import 方法。非常简单。

    Package google::protobuf::compiler 中提供了以下几个类,用来表示一个 .proto 文件中定义的 message,以及 Message 中的 field,如图所示。
    在这里插入图片描述
    类 FileDescriptor 表示一个编译后的 .proto 文件;类 Descriptor 对应该文件中的一个 Message;类 FieldDescriptor 描述一个 Message 中的一个具体 Field。

    比如编译完 lm.helloworld.proto 之后,可以通过如下代码得到 lm.helloworld.id 的定义:

    const protobuf::Descriptor *desc =    importer_.pool()->FindMessageTypeByName(“lm.helloworld”); 
    const protobuf::FieldDescriptor* field =    desc->pool()->FindFileByName (“id”);
    

    通过 Descriptor,FieldDescriptor 的各种方法和属性,应用程序可以获得各种关于 Message 定义的信息。比如通过 field->name() 得到 field 的名字。这样,您就可以使用一个动态定义的消息了。

    编写新的proto编译器

    随 Google Protocol Buffer 源代码一起发布的编译器 protoc 支持 3 种编程语言:C++,java 和 Python。但使用 Google Protocol Buffer 的 Compiler 包,您可以开发出支持其他语言的新的编译器。

    类 CommandLineInterface 封装了 protoc 编译器的前端,包括命令行参数的解析,proto 文件的编译等功能。您所需要做的是实现类 CodeGenerator 的派生类,实现诸如代码生成等后端工作。
    程序的大体框架如图所示:
    在这里插入图片描述
    在 main() 函数内,生成 CommandLineInterface 的对象 cli,调用其 RegisterGenerator() 方法将新语言的后端代码生成器 yourG 对象注册给 cli 对象。然后调用 cli 的 Run() 方法即可。这样生成的编译器和 protoc 的使用方法相同,接受同样的命令行参数,cli 将对用户输入的 .proto 进行词法语法等分析工作,最终生成一个语法树。该树的结构如图所示。
    在这里插入图片描述
    其根节点为一个 FileDescriptor 对象(请参考“动态编译”一节),并作为输入参数被传入 yourG 的 Generator() 方法。在这个方法内,您可以遍历语法树,然后生成对应的您所需要的代码。简单说来,要想实现一个新的 compiler,您只需要写一个 main 函数,和一个实现了方法 Generator() 的派生类即可。

    Protobuf 的更多细节

    Google Protocol Buffer 的 Encoding

    Protobuf 序列化后所生成的二进制消息非常紧凑,这得益于 Protobuf 采用的非常巧妙的 Encoding 方法。考察消息结构之前,让我首先要介绍一个叫做 Varint 的术语。Varint 是一种紧凑的表示数字的方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。比如对于 int32 类型的数字,一般需要 4 个 byte 来表示。但是采用 Varint,对于很小的 int32 类型的数字,则可以用 1 个 byte 来表示。当然凡事都有好的也有不好的一面,采用 Varint 表示法,大的数字则需要 5 个 byte 来表示。
    Varint 中的每个 byte 的最高位 bit 有特殊的含义,如果该位为 1,表示后续的 byte 也是该数字的一部分,如果该位为 0,则结束,其他的 7 个 bit 都用来表示数字。因此小于 128 的数字都可以用一个 byte 表示。大于 128 的数字,比如 300,会用两个字节来表示:1010 1100 0000 0010。
    下图演示了 Google Protocol Buffer 如何解析两个 bytes。注意到最终计算前将两个 byte 的位置相互交换过一次,这是因为 Google Protocol Buffer 字节序采用 little-endian 的方式。
    在这里插入图片描述
    消息经过序列化后会成为一个二进制数据流,该流中的数据为一系列的 Key-Value 对。如下图所示:
    在这里插入图片描述
    采用这种 Key-Pair 结构无需使用分隔符来分割不同的 Field。对于可选的 Field,如果消息中不存在该 field,那么在最终的 Message Buffer 中就没有该 field,这些特性都有助于节约消息本身的大小。

    举例:

    Test1.id = 10;
    Test1.str = “hello”;
    

    则最终的 Message Buffer 中有两个 Key-Value 对,一个对应消息中的 id;另一个对应 str。Key 用来标识具体的 field,在解包的时候,Protocol Buffer 根据 Key 就可以知道相应的 Value 应该对应于消息中的哪一个 field。
    Key 的定义如下:(field_number << 3) | wire_type

    可以看到 Key 由两部分组成。第一部分是 field_number,比如消息 lm.helloworld 中 field id 的 field_number 为 1。第二部分为 wire_type。表示 Value 的传输类型。
    Wire Type 可能的类型如下表所示:

    TypeMeaningUsed For
    0Varintint32, int64, uint32, uint64, sint32, sint64, bool, enum
    164-bitfixed64, sfixed64, double
    2Length-delimistring, bytes, embedded messages, packed repeated fields
    3Start groupGroups (deprecated)
    4End groupGroups (deprecated)
    532-bitfixed32, sfixed32, float

    在我们的例子当中,field id 所采用的数据类型为 int32,因此对应的 wire type 为 0。细心的读者或许会看到在 Type 0 所能表示的数据类型中有 int32 和 sint32 这两个非常类似的数据类型。Google Protocol Buffer 区别它们的主要意图也是为了减少 encoding 后的字节数。在计算机内,一个负数一般会被表示为一个很大的整数,因为计算机定义负数的符号位为数字的最高位。如果采用 Varint 表示一个负数,那么一定需要 5 个 byte。为此 Google Protocol Buffer 定义了 sint32 这种类型,采用 zigzag 编码。Zigzag 编码用无符号数来表示有符号数字,正数和负数交错,这就是 zigzag 这个词的含义了。
    在这里插入图片描述
    使用 zigzag 编码,绝对值小的数字,无论正负都可以采用较少的 byte 来表示,充分利用了 Varint 这种技术。
    其他的数据类型,比如字符串等则采用类似数据库中的 varchar 的表示方法,即用一个 varint 表示长度,然后将其余部分紧跟在这个长度部分之后即可。

    通过以上对 protobuf Encoding 方法的介绍,想必您也已经发现 protobuf 消息的内容小,适于网络传输。假如您对那些有关技术细节的描述缺乏耐心和兴趣,那么下面这个简单而直观的比较应该能给您更加深刻的印象。
    对于代码清单 1中的消息,用 Protobuf 序列化后的字节序列为:08 65 12 06 48 65 6C 6C 6F
    而如果用 XML,则类似这样:

    31 30 31 3C 2F 69 64 3E 3C 6E 61 6D 65 3E 68 65
     6C 6C 6F 3C 2F 6E 61 6D 65 3E 3C 2F 68 65 6C 6C
    6F 77 6F 72 6C 64 3E  
    一共 55 个字节,这些奇怪的数字需要稍微解释一下,其含义用 ASCII 表示如下:
     <helloworld>    
     	 <id>101</id>    
     	  <name>hello</name>  
    </helloworld>
    

    封解包过程

    以代码清单中的 Reader 为例,该程序首先调用 msg1 的 ParseFromIstream 方法,这个方法解析从文件读入的二进制数据流,并将解析出来的数据赋予 helloworld 类的相应数据成员。

    在这里插入图片描述
    整个解析过程需要 Protobuf 本身的框架代码和由 Protobuf 编译器生成的代码共同完成。Protobuf 提供了基类 Message 以及 Message_lite 作为通用的 Framework,,CodedInputStream 类,WireFormatLite 类等提供了对二进制数据的 decode 功能,Protobuf 的解码可以通过几个简单的数学运算完成,无需复杂的词法语法分析,因此 ReadTag() 等方法都非常快。

    展开全文
  • protobuf已经全面迁移到github,地址:https://github.com/google/protobuf 直接下载2.6.1版本:https://github.com/google/protobuf/archive/v2.6.1.zip $wget ...

    说明:
    protobuf已经全面迁移到github,地址:https://github.com/google/protobuf
    直接下载2.6.1版本:https://github.com/google/protobuf/archive/v2.6.1.zip

    $wget https://github.com/google/protobuf/archive/v2.6.1.zip
    $unzip protobuf-2.6.1.zip
    $cd protobuf-2.6.1
    vim autogen.sh
    

    下载自github的代码需要首先执行 $ ./autogen.sh 生成configure文件
    注意autogen.sh 需要gtest包,默认是从 googletest.googlecode.com下载,国内需要翻墙才能访问,很多人问autogen.sh运行失败,这里我补充一下
    修改 autogen.sh
    原来的:

    echo "Google Test not present.  Fetching gtest-1.5.0 from the web..."
     curl http://googletest.googlecode.com/files/gtest-1.5.0.tar.bz2 | tar jx
     mv gtest-1.5.0 gtest
    

    修改之后:

    wget https://github.com/google/googletest/archive/release-1.5.0.tar.gz
    tar xzvf release-1.5.0.tar.gz
    mv googletest-release-1.5.0 gtest
    

    在这里插入图片描述
    运行./autogen.sh 生成 configure
    自定义安装路径:

    protobuf默认安装在 /usr/local 目录
    你可以修改安装目录通过 ./configure --prefix=命令
    虽然我是root用户但觉得默认安装过于分散,所以统一安装在/usr/local/protobuf下
    
    $./configure --prefix=/usr/local/protobuf
    $ make
    $ make check
    $ make install
    
    到此步还没有安装完毕,在/etc/profile 或者用户目录 ~/.bash_profile
    添加下面内容
    ####### add protobuf lib path ########
    #(动态库搜索路径) 程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/protobuf/lib/
    #(静态库搜索路径) 程序编译期间查找动态链接库时指定查找共享库的路径
    export LIBRARY_PATH=$LIBRARY_PATH:/usr/local/protobuf/lib/
    #执行程序搜索路径
    export PATH=$PATH:/usr/local/protobuf/bin/
    #c程序头文件搜索路径
    export C_INCLUDE_PATH=$C_INCLUDE_PATH:/usr/local/protobuf/include/
    #c++程序头文件搜索路径
    export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/protobuf/include/
    #pkg-config 路径
    export PKG_CONFIG_PATH=/usr/local/protobuf/lib/pkgconfig/
    ######################################
    
    

    cd /data0/zhenxin/tools/protobuf-2.6.1/src
    vim QueryByMids.proto

    option java_package = "com.sina";
    option java_generic_services = true;
    option java_generate_equals_and_hash = true;
    
    message MidRequest {
      required int32 date = 1;
      repeated int64 mids = 2;
    }
    
    message MidResult {
      required int32 id = 1;
      required int64 size = 2;
    }
    
    message MidResponse {
      repeated MidResult results = 1;
    }
    
    service QueryByMidsService {
      rpc getResult(MidRequest)
        returns (MidResponse);
    }
    
    
    ./protoc --java_out=. QueryByMids.proto
    

    在这里插入图片描述在这里插入图片描述

    在这里插入图片描述

    至此如果全部正确:代码不会出现红色了

    展开全文
  • Linux下使用protobuf-c实现自定义协议

    千次阅读 2018-05-12 20:37:36
    [1] https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html [2] https://code.google.com/archive/p/thrift-protobuf-compare/wikis/Benchmarking.wiki [3] ...

    1、背景需求

        通信协议设计,考虑了后续跨语言的支持(如Java、Python、C),需求一种序列化、反序列化的库

    2、相关知识

        Google Protocol BUffer 提供了一种适用于RPC系统、持续数据存储系统的混合语言数据标准,可用于通信协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前有C++、C、Java、Python三种语言的API。

    2.1 protobuf的优点

        1、通过数据结构的定义,能够生成结构相关的接口代码;

        2、兼容性好,支持对现有数据结构添加新成员;

        3、协议文本字段自动压缩,使用二进制传输;

        详细的介绍可见参考文献[1],附上一张序列化技术相关的性能对比图:


    2.2 数据格式

        关于proto2还是proto3的选择可参考文献[4],考虑兼容性的问题,该文章采用的proto2方式定义[5];

        

    .proto类型

    Java 类型

    C++类型

    备注

    double

    double

    double

     

    float

    float

    float

     

    int32

    int

    int32

    使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。

    int64

    long

    int64

    使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64。

    uint32

    int[1]

    uint32

    Uses variable-length encoding.

    uint64

    long[1]uint64Uses variable-length encoding.

    sint32

    int

    int32

    使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。

    sint64

    long

    int64

    使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。

    fixed32

    int[1]

    uint32

    总是4个字节。如果数值总是比总是比228大的话,这个类型会比uint32高效。

    fixed64

    long[1]

    uint64

    总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。

    sfixed32

    int

    int32

    总是4个字节。

    sfixed64

    long

    int64

    总是8个字节。

    bool

    boolean

    bool

     

    string

    String

    string

    一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。

    bytes

    ByteString

    string

    可能包含任意顺序的字节数据。

     

     2.3 安装   

        对于C语言的使用的方法,则需要安装 protobuf、protobuf-c两个安装包来实现(本文用的 protobuf-all-3.5.1.tar.gz、protobuf-c-1.3.0.tar.gz )。

            安装方式:./configure && make && make install


    3、实现

        使用 proto2 语法定义一个用户结构 user.proto,包含协议号(默认0x010000)、魔数(默认0xfb709394)、用户名、电话、状态、邮箱(可选)信息;

    syntax = "proto2";
    
    option optimize_for = SPEED;
    
    message User {
            required uint32 version = 1 [ default = 0x010000 ];
            required uint32 magic   = 2 [ default = 0xfb709394 ];
            required string name    = 3;
            required string phone   = 4;
            enum Status {
                    IDLE = 1;
                    BUSY = 2;
            };
            required Status stat    = 5 [ default = IDLE ];
            optional string email   = 6;
    }

        执行命令 ``protoc-c --c_out=. user.proto`` 后将生成user.pb-c.c、user.pb-c.h两个文件,编译的时候需要加上 -lprotobuf-c 选项。

        简单看下生成的文件接口,主要就是:

        结构体: struct  _User 

        相关接口:user__init、user__pack、user__unpack、user__free_unpacked

    /* Generated by the protocol buffer compiler.  DO NOT EDIT! */
    /* Generated from: user.proto */
    
    #ifndef PROTOBUF_C_user_2eproto__INCLUDED
    #define PROTOBUF_C_user_2eproto__INCLUDED
    
    #include <protobuf-c/protobuf-c.h>
    
    PROTOBUF_C__BEGIN_DECLS
    
    #if PROTOBUF_C_VERSION_NUMBER < 1000000
    # error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
    #elif 1003000 < PROTOBUF_C_MIN_COMPILER_VERSION
    # error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
    #endif
    
    
    typedef struct _User User;
    
    
    /* --- enums --- */
    
    typedef enum _User__Status {
      USER__STATUS__IDLE = 1,
      USER__STATUS__BUSY = 2
        PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(USER__STATUS)
    } User__Status;
    
    /* --- messages --- */
    
    struct  _User
    {
      ProtobufCMessage base;
      uint32_t version;
      uint32_t magic;
      char *name;
      char *phone;
      User__Status stat;
      char *email;
    };
    #define USER__INIT \
     { PROTOBUF_C_MESSAGE_INIT (&user__descriptor) \
        , 65536u, 4218459028u, NULL, NULL, USER__STATUS__IDLE, NULL }
    
    
    /* User methods */
    void   user__init
                         (User         *message);
    size_t user__get_packed_size
                         (const User   *message);
    size_t user__pack
                         (const User   *message,
                          uint8_t             *out);
    size_t user__pack_to_buffer
                         (const User   *message,
                          ProtobufCBuffer     *buffer);
    User *
           user__unpack
                         (ProtobufCAllocator  *allocator,
                          size_t               len,
                          const uint8_t       *data);
    void   user__free_unpacked
                         (User *message,
                          ProtobufCAllocator *allocator);
    /* --- per-message closures --- */
    
    typedef void (*User_Closure)
                     (const User *message,
                      void *closure_data);
    
    /* --- services --- */
    
    
    /* --- descriptors --- */
    
    extern const ProtobufCMessageDescriptor user__descriptor;
    extern const ProtobufCEnumDescriptor    user__status__descriptor;
    
    PROTOBUF_C__END_DECLS
    
    
    #endif  /* PROTOBUF_C_user_2eproto__INCLUDED */

    使用实例,这里相对就方便多了,序列化:

    static size_t __do_pack(u8 *buffer)
    {
            User user;
    
            user__init(&user);
    
            user.name   = "zhangsan";
            user.phone  = "010-1234-5678";
            user.email  = "zhangsan@123.com";
            user.stat   = USER__STATUS__IDLE;
    
            return user__pack(&user, buffer);
    }

    反序列化,注意 xx_unpack 接口是会申请空间后返回指针出来,使用完成后需调用 xx__free_unpacked 进行释放:

    static int __do_unpack(const u8 *buffer, size_t len)
    {
            User *pusr = user__unpack(NULL, len, buffer);
            if (!pusr) {
                    printf("user__unpack failed\n");
                    return FAILURE;
            }
    
            assert(pusr->magic == MAGIC);
            assert(pusr->version == VERSION);
            printf("Unpack: %s %s %s\n", pusr->name, pusr->phone, pusr->email);
    
            user__free_unpacked(pusr, NULL);
            return SUCCESS;
    }
    int main(int argc, char *argv[])
    {
            u8 buffer[1024] = {0}; 
            size_t size = __do_pack(buffer);
            printf("Packet size: %zd\n", size);
            __do_unpack(buffer, size);
    
            exit(EXIT_SUCCESS);
    }

    执行结果为:

    Packet size: 55
    Unpack: zhangsan 010-1234-5678 zhangsan@123.com

    使用gdb打印序列化后的buffer内容

    (gdb) b __do_pack  
    Breakpoint 1 at 0x4008f4: file user.cc, line 24.
    (gdb) r
    Starting program: /home/liujinfeng/git/filesync/tests/test_redis/user 
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/libthread_db.so.1".
    
    Breakpoint 1, __do_pack (buffer=0x7fffffffe230 "") at user.cc:24
    24              user__init(&user);
    (gdb) finish
    Run till exit from #0  __do_pack (buffer=0x7fffffffe230 "") at user.cc:24
    0x0000000000400a38 in main (argc=1, argv=0x7fffffffe728) at user.cc:53
    53              size_t size = __do_pack(buffer);
    Value returned is $1 = 55
    (gdb) p buffer
    $2 = "\b\200\200\004\020\224\247\302\333\017\032\bzhangsan\"\r010-1234-5678(\001\062\020zhangsan@123.com", '\000' <repeats 968 times>
    (gdb) 

    发现数值部分会进行压缩处理(Zigzag),但字符串那块并没有进行压缩。

    然后对size进行改变,内部调用unpack会返回NULL,这块需要注意一下。

    (gdb) p __do_unpack(buffer, 30) 
    user__unpack failed
    $6 = -1
    (gdb) p __do_unpack(buffer, 60)  
    user__unpack failed
    $7 = -1
    

    4、结论

    protobuf的哲学在于定义结构标准,使用工具生成代码接口,达到跨语言的目的;

    协议内容那块,对于数字组合能有效进行压缩,但字符串方面不处理,可以考虑结合libz进行压缩处理;


    参考文章:

    [1] https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html

    [2] https://code.google.com/archive/p/thrift-protobuf-compare/wikis/Benchmarking.wiki

    [3] https://blog.csdn.net/kid_2412/article/details/52502567

    [4] https://solicomo.com/network-dev/protobuf-proto3-vs-proto2.html

    [5] https://blog.csdn.net/zhaozheng7758/article/details/6749047


    展开全文
  • 安装protobuf首先需要考虑自己的linux主机能否上外网,如果能上外网,则只需要执行一条命令即可,不能的话,则需要执行多条命令 1.测试自己的主机能否上外网 ping google.com,大部分Linux服务器由于安全性等方面的...

    安装protobuf首先需要考虑自己的linux主机能否上外网,如果能上外网,则只需要执行一条命令即可,不能的话,则需要执行多条命令

    1.测试自己的主机能否上外网

    ping google.com,大部分Linux服务器由于安全性等方面的原因,会限制无法上外网

    2.可以上外网则执行

    go get github.com/golang/protobuf/proto,这条命令本质上是先执行 git clone下载文件,然后执行go install安装软件包

    3.如果不能上外网,则执行下面的语句

    • (1)下载protobuf源码
      git clone https://github.com/golang/protobuf.git
    • (2)将protobuf文件放在$GOPATH/src/github.com/golang/(没有则自己创建此目录)
    • (3)进入protobuf目录,执行make命令,会出现
      go install ./proto ./jsonpb ./ptypes ./protoc-gen-go,系统已经将我们需要的包都给install了,此时在执行 go build就不会出现此问题了

    备注 go开发过程中,出现此类问题,绝大多数都是由于包路径问题,我们需要关注GOPATH、GOROOT以及安装包的路径,大部分问题都能迎刃而解。

    展开全文
  • protobuf开发环境搭建

    2017-12-03 19:01:50
    protobuf开发环境搭建 1.1 Window 1.1.1下载protobuf包 参考地址:https://github.com/google/protobuf 1.1.2编译 1. 建立build_vs_project目录,使用cmake-gui分别构建编译x64/x86平台Debug/Release工程 ...
  • 前言 之前分享关于JSON的使用:例说嵌入式实用知识之JSON数据。JSON类型数据可读性很好,但是整个数据包中会带有一些无用的... Protocol Buffers,是Google公司开发的一种数据格式,类似于XML能够将结构化数据序列化.
  • 最近项目中需要用到stm32与H6(移植了linux)进行数据交互,H6端是用C++编写的串口底层驱动,与stm32的串口连接并通信。-串口间的通信协议定为采用protobuf打包数据并通过串口发出的形式,即发送端编码数据并序列化...
  • 之前写过一篇博文:《如果终端采用protobuf与采集前置通信,能带来哪些变革?...那么接下来在嵌入式linux之go语言开发实战中,也尝试用protobuf作为序列化和通信的协议格式。 之前想做个protobuf序列化的反向解...
  • 为了使用源码编译protobuf,需要下面的工具: autoconf, automake, libtool, make, g++, unzip 如果你使用ubuntu/debian,你可以使用如下方式安装这些工具: $ sudo apt-get install autoconf automake libtool ...
  • linux下使用protobuf

    2019-05-28 20:19:16
    下载地址: protobuf:https://code.google.com/p/protobuf/downloads/list下载最新的protobuf 我这里下载的是2.5.0 protoc-gen-lua:https://github.com/sean-lin/protoc-gen-lua下载master分...
  • Google ProtobufLinux Socket通讯中的应用.pdf
  • Linux + protobuf + QT 兼容性问题解决

    千次阅读 2018-06-12 17:00:10
    问题现象 问题一: protobuf.internal.lock...[libprotobuf FATAL google/protobuf/stubs/common.cc:79] This program was compiled against version 2.6.1 of the Protocol Buffer runtime library, which is no...
  • 免费学习视频链接点击:C/C++Linux服务器开发高级架构师/Linux后台架构师 02 数据结构与算法 【回放】面试必聊的排序与KMP(1月7日 20:00-22:00) 【回放】随处可见的红黑树(1月9日 20:00-22:00) 【回放】磁盘...
  • 1.首先到boost官网(http://www.boost.org/users/download/)下载相应版本(boost_1_61_0)的boost源码包,将源码包放到linux服务器指定目录, 如果源码包是以.zip结尾用unzip解压,如果是.tar.gz等结尾用t...
  • protobuf使用linux

    2017-10-29 13:36:13
    描述了以下protobuf的概念和使用
  • 通讯协议中使用protobuf的好处: 可以跨平台传输数据;...protobufLinux下安装说明: sudo apt-get install autoconf automake libtool curl make g++ unzip git clone https://github.com/google/protobuf.g
  • linux下使用protobuf实现简单配置功能

    千次阅读 2015-04-07 17:41:02
     程序一般需要load一些参数列表,一般来说我们可以通过linux自带的命令行解析函数来搞定(getopt_long,如果需要了解的man一 下,manpage里面是有example的),但是对于参数太多,我们不可能写满一屏幕进行传参吧,...
  • 什么是protobuf-c 之前的文章:《Protobuf:一种更小、更快、更高效的协议》详细介绍了protobufprotobuf-c。这里再简单提一下:Protocol Buffe...
  • 目录 ...2、linux下安装google protobuf 1、protobuf用法 1、编写*.proto文件(UserInfo.proto) package Test; message userninfo { required string name = 1; required string mail = 2; r...
  • Linux开发工程师-Linux后台开发工程师-Linux高级互联网架构师。 想必大家都知道从事后台开发首先就是要选择一种语言,小编今天跟大家分享是用C/C++ 做的后台开发。所以想从事这方面的朋友得有C/C++的基础。 ...
  • UE4 接入 Protobuf(C++ 层)

    千次阅读 2020-02-28 12:55:07
    在网络游戏开发中,我们通常使用 Google Protobuf 作为网络协议定制的格式,那么在 UE4 中如何集成 Protobuf 是我们接下来要做的事情。 开源示例 code4game/libprotobuf_ue4 jashking/UE4Protobuf 看过...
  • protobuf是google团队开发的用于高效存储和读取结构化数据的工具。相比于json和xml,protobuf会把数据压缩得更小,大约是json格式的1/10,xml格式的1/20。正因如此,protobuf编码后的数据,不能像json、xml那样直观...
  • VS2010及以上版本
  • ProtoBuf2Leaf this is a demo about protobuf call and receive leaf tips:This demo is only developed and debugged in ...tips:此demo只是windows环境下开发和调试的,在linux下面可能会出现问题未知的问题。
  • Protobuf是google推出的一种数据协议,它支持多语言(java、python、C++等等)、多平台(linux、win、mac等等);Protobuf简称proto,当前主要有proto2、proto3两个版本,推荐使用新版本的proto3。本文主要介绍使用...
  • PHP安装protobuf详解

    2018-01-15 10:30:23
    PHP作为WEB开发的主力居然没有得到google protocol buffer的官方支持实在是有一些遗憾。 目前PHP支持google protocol buffer的开源实现包括pb4php、phpbuf等。 RPC(Remote Procedure Call Protocol)——远程过程...
  • Linux下安装protobuf

    2014-06-09 22:34:42
    一、protobuf简介:Google发明的一种全新的数据交换格式 当前分布式Web系统之间交换数据的所用数据格式的类型中,最常见的依然是json与XML。不过Google的技术大牛们认为json/XML这些文本类型的数据格式传输效率太...
  • C/C++Linux服务器开发/后台架构师知识体系1. 精进基石专栏1.1 数据结构与算法面试必聊的排序与KMP随处可见的红黑树磁盘存储链式的B树与B+树海量数据去重的Hash与布隆过滤器,bitmap图论算法,di jkstra,dfs,bfs,...
  • Linux开发工程师-Linux后台开发工程师-Linux高级互联网架构师。想必大家都知道从事后台开发首先就是要选择一种语言,小编今天跟大家分享是用C/C++ 做的后台开发。所以想从事这方面的朋友得有C/C++的基础。 首先跟...
  • 关于什么是protobuf,网上搜搜一大堆,很多人用的都还是...protobuf很出名,是google开发的序列化库,很多公司都使用它作为接口的数据结构。地址:https://developers.google.com/protocol-buffers/ 支持java、c++、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,202
精华内容 4,080
关键字:

linuxprotobuf开发

linux 订阅