当前位置: 首页 > news >正文

【Protocol Buffer】Windows Protocol Buffer 使用教程[实践中...]

需要VS2012 以上,既支持C++11

实践过程中,遇到困难不行,只有这个可以:https://blog.csdn.net/PROGRAM_anywhere/article/details/77365876

protobuff 生成的接口函数说明:

http://www.cnblogs.com/stephen-liu74/archive/2013/01/04/2842533.html

//——————————————————————————————————————————————————

protocolbuffer 简介

protobuf 和protoc的区别

protobuf:

protoc:

使用

1.      下载源码。

2.      编译源码生产解析库。

3.      生产include文件。

4.    编写*.proto文件

5、用protoc 根据.proto 文件 生产 .proto定义的数据的 解析方法函数接口文件

6、将生成的文件加入到工程中


protocolbuffer 简介

(略)https://github.com/protocolbuffers/protobuf

查protobuf的使用说明:

官网上给出的地址是:https://developers.google.com/protocol-buffers/ 但是因为谷歌在国内无法访问,所以这个链接不能访问,

但是谷歌已经开通中国服务器,所以将com改为cn即可:https://developers.google.cn/protocol-buffers/

安装

安装介绍在网站的 README 文件中.

安装protobuf 包含2部分:

1、protocol 编译器(就是protoc.exe,用来编译 .proto文件)

2、protobuf  运行时(运行库/环境) (你选择的那个语言的对应的运行库)

1、protocol 编译器

就是protoc.exe (windows下,linux下没有后缀)

方法1:C++语言环境用,是下载代码,然后自己编译安装(一般linux都是这样,网上的教程大多是这个)

不过linux自己安装编译因为各种依赖,一次成功率不高(特别是老的linux),所以我自己就不编译了,我用的方法2

方法2:直接下载官方提供的已经编好的。在https://github.com/protocolbuffers/protobuf/releases 下载,反正是用来编译

.proto 文件的,能用就行。一般包的名是: protoc-$VERSION-$PLATFORM.zip. ,你在windows下处理你的.proto文件,就下载.win32 版的。

2、protobuf  运行库

运行库的安装见:点击对应语言的后面圈起来的部分进去就能看到

例如C++,点 src进去

https://github.com/protocolbuffers/protobuf/tree/master/src

我在windows下编译我的程序,所以我安装在windows上,所以看

里面第一部分是protoc的获取,我们已经说过。

第二部分是protobuf 运行时的安装,介绍的是vcpkg命令的安装,vcpkg是什么可以看这个文章:

https://blog.csdn.net/cjmqas/article/details/79282847#1-为什么要用vcpkg

下载vcpkg包裹后解压,点击里面的bootstrap-vcpkg.bat生成vcpkg.exe ,在打开命令行(文章推荐在powershell,但是在powershell没反应),切换到vcpkg-master目录下(即vcpkg.exe 所在的目录) 执行命令安装:

vcpkg.exe install protobuf[zlib] protobuf[zlib]:x64-windows

(也可以用下载源码,然后用cmake编译的方法安装,参考:

3、protobuf 和protoc的区别

protobuf:

源码,编译成库,这些库就是解析protocolbuffer格式的数据的库,给工程调用

protobuf-2.6.1.tar.bz2 :(protobuf所有的源码都在里面,用它进行编译成lib库)

protoc:

protoc 就是将.proto 文件转成对应语言的方法/类的程序,可以下载windows版直接windows电脑上转换,也可以protobuf编译后也生成protoc 也可以用来转换.proto

protoc-2.6.1-win32.zip(内涵protoc.exe,用来把proto文件编译成目标语言(C++,Java,Python)的文本,是google protobuf定义的格式。其实这个文件不是必须的,编译protobuf-2.6.1.tar.bz2的时候会生成这个exe)

使用

1.      下载源码。

     从http://code.google.com/p/protobuf/downloads/list上下载protobuf-2.5.0.zip。

2.      编译源码生产解析库。

      解压并打开目录下的protobuf-2.5.0\vsprojecs\protobuf.sln工程,生成解决方案。在dubug或release目录下会生成几个.lib文件和.exe文件。(对于每个版本应该使用两次“生成解决方案”,因为其中有一些依赖项目,两次后,便可编译所有的项目)要使用的库文件包括:libprotobuf.lib、libprotoc.lib、libprotobuf-lite.lib,另外还有一个protoc.exe文件。其他还包括了一些测试程序。

3.      生产include文件。

执行extract_includes.bat 文件,生成include文件夹。

4.    编写*.proto文件(UserInfo.proto)

package Test;
message userninfo
{
required string name = 1;
required string mail = 2;
required string time = 3;
optional string status = 4;
}

*.proto的编写规则见后面

5、用protoc 处理[*.proto文件]生成 [*.proto]定义的数据对应的解析函数接口文件

(如:protoc命令处理UserInfo.proto生成 解析userninfo数据的函数的文件:user.pb.cc、user.pb.h。)

   将*.proto放到protoc目录下,打开命令行窗口(cmd)切换到protoc目录下,输入命令:

protoc  --cpp_out=.\  .\user.proto

-cpp_out= 后面加生成的文件放哪个目录 

-cpp_out表示转成c++ 对应的解析方法文件,其他语言见:(--csharp_out据说要下载专门的csharp的protoc才可以支持)

 --cpp_out=OUT_DIR           Generate C++ header and source. 
  --csharp_out=OUT_DIR        Generate C# source file. 
  --java_out=OUT_DIR          Generate Java source file. 
  --javanano_out=OUT_DIR      Generate Java Nano source file. 
  --js_out=OUT_DIR            Generate JavaScript source. 
  --objc_out=OUT_DIR          Generate Objective C header and source. 
  --python_out=OUT_DIR        Generate Python source file. 
  --ruby_out=OUT_DIR          Generate Ruby source file.

如果*.proto 依赖其他*.proto,则需要加参数 -I,如A.proto依赖B.proto 则:

protoc -I=.\  --cpp_out=.\  .\user.proto   (相当于import .\ 目录下的所以依赖文件)

6、将生成的文件加入到工程中

将libprotobuf.lib、libprotoc.lib、libprotobuf-lite.lib 添加到工程依赖目录中,专业就可以调用生成的文件内的方法解析数据了。

参考下面的两篇文章:

https://blog.csdn.net/yaoyuanyylyy/article/details/36389277

https://blog.csdn.net/majianfei1023/article/details/45371743

不想自己编译protoc的可以看这个《protoc 命令的获得》:

https://www.cnblogs.com/ghj1976/p/5435565.html

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

之前在网络通信和通用数据交换等应用场景中经常使用的技术是 JSON 或 XML,而在最近的开发中接触到了 Google 的 ProtoBuf。

在查阅相关资料学习 ProtoBuf 以及研读其源码之后,发现其在效率、兼容性等方面非常出色。在以后的项目技术选型中,尤其是网络通信、通用数据交换等场景应该会优先选择 ProtoBuf。

自己在学习 ProtoBuf 的过程中翻译了官方的主要文档,一来当然是在学习 ProtoBuf,二来是培养阅读英文文档的能力,三来是因为 Google 的文档?不存在的!

看完这些文档对 ProtoBuf 应该就有相当程度的了解了。

翻译文档见 [索引]文章索引,导航为翻译 - 技术 - ProtoBuf 官方文档。

但是官方文档更多的是作为查阅和权威参考,并不意味着看完官方文档就能立马理解其原理。

本文以及接下来的几篇文章会对 ProtoBuf 的编码、序列化、反序列化、反射等原理做一些详细介绍,同时也会尽量将这些原理表达的更为通俗易懂。

何为 ProtoBuf

我们先来看看官方文档给出的定义和描述:

protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。

Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。

简单来讲, ProtoBuf 是结构数据序列化[1] 方法,可简单类比于 XML[2],其具有以下特点:

  • 语言无关、平台无关。即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台
  • 高效。即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单
  • 扩展性、兼容性好。你可以更新数据结构,而不影响和破坏原有的旧程序

序列化[1]:将结构数据对象转换成能够被存储和传输(例如网络传输)的格式,同时应当要保证这个序列化结果在之后(可能在另一个计算环境中)能够被重建回原来的结构数据或对象。
更为详尽的介绍可参阅 维基百科。类比于 XML[2]:这里主要指在数据通信和数据存储应用场景中序列化方面的类比,但个人认为 XML 作为一种扩展标记语言和 ProtoBuf 还是有着本质区别的。

使用 ProtoBuf

对 ProtoBuf 的基本概念有了一定了解之后,我们来看看具体该如何使用 ProtoBuf。第一步,创建 .proto 文件,定义数据结构,如下例1所示:

// 例1: 在 xxx.proto 文件中定义 Example1 message
message Example1 {
    optional string stringVal = 1;
    optional bytes bytesVal = 2;
    message EmbeddedMessage {
        int32 int32Val = 1;
        string stringVal = 2;
    }
    optional EmbeddedMessage embeddedExample1 = 3;
    repeated int32 repeatedInt32Val = 4;
    repeated string repeatedStringVal = 5;
}

我们在上例中定义了一个名为 Example1 的 消息,语法很简单,message 关键字后跟上消息名称:

message xxx {

}

之后我们在其中定义了 message 具有的字段,形式为:

message xxx {
  // 字段规则:required -> 字段只能也必须出现 1 次
  // 字段规则:optional -> 字段可出现 0 次或1次
  // 字段规则:repeated -> 字段可出现任意多次(包括 0)
  // 类型:int32、int64、sint32、sint64、string、32-bit ....
  // 字段编号:0 ~ 536870911(除去 19000 到 19999 之间的数字)
  字段规则 类型 名称 = 字段编号;
}

在上例中,我们定义了:

  • 类型 string,名为 stringVal 的 optional 可选字段,字段编号为 1,此字段可出现 0 或 1 次
  • 类型 bytes,名为 bytesVal 的 optional 可选字段,字段编号为 2,此字段可出现 0 或 1 次
  • 类型 EmbeddedMessage(自定义的内嵌 message 类型),名为 embeddedExample1 的 optional 可选字段,字段编号为 3,此字段可出现 0 或 1 次
  • 类型 int32,名为 repeatedInt32Val 的 repeated 可重复字段,字段编号为 4,此字段可出现 任意多次(包括 0)
  • 类型 string,名为 repeatedStringVal 的 repeated 可重复字段,字段编号为 5,此字段可出现 任意多次(包括 0)

关于 proto2 定义 message 消息的更多语法细节,例如具有支持哪些类型,字段编号分配、import
导入定义,reserved 保留字段等知识请参阅 [翻译] ProtoBuf 官方文档(二)- 语法指引(proto2)。

关于定义时的一些规范请参阅 [翻译] ProtoBuf 官方文档(四)- 规范指引

第二步,protoc 编译 .proto 文件生成读写接口

我们在 .proto 文件中定义了数据结构,这些数据结构是面向开发者和业务程序的,并不面向存储和传输。

当需要把这些数据进行存储或传输时,就需要将这些结构数据进行序列化、反序列化以及读写。那么如何实现呢?不用担心, ProtoBuf 将会为我们提供相应的接口代码。如何提供?答案就是通过 protoc 这个编译器。

可通过如下命令生成相应的接口代码:

// $SRC_DIR: .proto 所在的源目录
// --cpp_out: 生成 c++ 代码
// $DST_DIR: 生成代码的目标目录
// xxx.proto: 要针对哪个 proto 文件生成接口代码

protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/xxx.proto

最终生成的代码将提供类似如下的接口:

例子-序列化和解析接口.png

例子-protoc 生成接口.png

第三步,调用接口实现序列化、反序列化以及读写
针对第一步中例1定义的 message,我们可以调用第二步中生成的接口,实现测试代码如下:

//
// Created by yue on 18-7-21.
//
#include <iostream>
#include <fstream>
#include <string>
#include "single_length_delimited_all.pb.h"

int main() {
    Example1 example1;
    example1.set_stringval("hello,world");
    example1.set_bytesval("are you ok?");

    Example1_EmbeddedMessage *embeddedExample2 = new Example1_EmbeddedMessage();

    embeddedExample2->set_int32val(1);
    embeddedExample2->set_stringval("embeddedInfo");
    example1.set_allocated_embeddedexample1(embeddedExample2);

    example1.add_repeatedint32val(2);
    example1.add_repeatedint32val(3);
    example1.add_repeatedstringval("repeated1");
    example1.add_repeatedstringval("repeated2");

    std::string filename = "single_length_delimited_all_example1_val_result";
    std::fstream output(filename, std::ios::out | std::ios::trunc | std::ios::binary);
    if (!example1.SerializeToOstream(&output)) {
        std::cerr << "Failed to write example1." << std::endl;
        exit(-1);
    }

    return 0;
}

关于 protoc 的使用以及接口调用的更多信息可参阅 [翻译] ProtoBuf 官方文档(九)- (C++开发)教程

关于例1的完整代码请参阅 源码:protobuf 例1。其中的 single_length_delimited_all.* 为例子相关代码和文件。

因为此系列文章重点在于深入 ProtoBuf 的编码、序列化、反射等原理,关于 ProtoBuf 的语法、使用等只做简单介绍,更为详见的使用教程可参阅我翻译的系列官方文档。

关于 ProtoBuf 的一些思考

官方文档以及网上很多文章提到 ProtoBuf 可类比 XML 或 JSON。

那么 ProtoBuf 是否就等同于 XML 和 JSON 呢,它们是否具有完全相同的应用场景呢?

个人认为如果要将 ProtoBuf、XML、JSON 三者放到一起去比较,应该区分两个维度。一个是数据结构化,一个是数据序列化。这里的数据结构化主要面向开发或业务层面,数据序列化面向通信或存储层面,当然数据序列化也需要“结构”和“格式”,所以这两者之间的区别主要在于面向领域和场景不同,一般要求和侧重点也会有所不同。数据结构化侧重人类可读性甚至有时会强调语义表达能力,而数据序列化侧重效率和压缩。

从这两个维度,我们可以做出下面的一些思考。

XML 作为一种扩展标记语言,JSON 作为源于 JS 的数据格式,都具有数据结构化的能力。

例如 XML 可以衍生出 HTML (虽然 HTML 早于 XML,但从概念上讲,HTML 只是预定义标签的 XML),HTML 的作用是标记和表达万维网中资源的结构,以便浏览器更好的展示万维网资源,同时也要尽可能保证其人类可读以便开发人员进行编辑,这就是面向业务或开发层面的数据结构化

再如 XML 还可衍生出 RDF/RDFS,进一步表达语义网中资源的关系和语义,同样它强调数据结构化的能力和人类可读。

关于 RDF/RDFS 和语义网的概念可查询相关资料了解,或参阅 2-Answer 系列-本体构建模块(一) 和 3-Answer 系列-本体构建模块(二) ,文中有一些简单介绍。

JSON 也是同理,在很多场合更多的是体现了数据结构化的能力,例如作为交互接口的数据结构的表达。在 MongoDB 中采用 JSON 作为查询语句,也是在发挥其数据结构化的能力。

当然,JSON、XML 同样也可以直接被用来数据序列化,实际上很多时候它们也是这么被使用的,例如直接采用 JSON、XML 进行网络通信传输,此时 JSON、XML 就成了一种序列化格式,它发挥了数据序列化的能力。但是经常这么被使用,不代表这么做就是合理。实际将 JSON、XML 直接作用数据序列化通常并不是最优选择,因为它们在速度、效率、空间上并不是最优。换句话说它们更适合数据结构化而非数据序列化。

扯完 XML 和 JSON,我们来看看 ProtoBuf,同样的 ProtoBuf 也具有数据结构化的能力,其实也就是上面介绍的 message 定义。我们能够在 .proto 文件中,通过 message、import、内嵌 message 等语法来实现数据结构化,但是很容易能够看出,ProtoBuf 在数据结构化方面和 XML、JSON 相差较大,人类可读性较差,不适合上面提到的 XML、JSON 的一些应用场景。

但是如果从数据序列化的角度你会发现 ProtoBuf 有着明显的优势,效率、速度、空间几乎全面占优,看完后面的 ProtoBuf 编码的文章,你更会了解 ProtoBuf 是如何极尽所能的压榨每一寸空间和性能,而其中的编码原理正是 ProtoBuf 的关键所在,message 的表达能力并不是 ProtoBuf 最关键的重点。所以可以看出 ProtoBuf 重点侧重于数据序列化 而非 数据结构化

最终对这些个人思考做一些小小的总结:

  1. XML、JSON、ProtoBuf 都具有数据结构化数据序列化的能力
  2. XML、JSON 更注重数据结构化,关注人类可读性和语义表达能力。ProtoBuf 更注重数据序列化,关注效率、空间、速度,人类可读性差,语义表达能力不足(为保证极致的效率,会舍弃一部分元信息)
  3. ProtoBuf 的应用场景更为明确,XML、JSON 的应用场景更为丰富。


作者:404_89_117_101
链接:https://www.jianshu.com/p/a24c88c0526a
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章:

  • 【java】115-Java经典
  • 【Hbase】HBase入门教程
  • 【前端框架】前端框架学习
  • 【try……catch】C++ try…… catch 笔记(C语言中也可以Try-Catch异常处理)
  • 【消息中间件】Kafka、RabbitMQ、RocketMQ等消息中间件 学习笔记
  • 【Cmake】Cmake windows下的使用方法|linux 下|命令行用法
  • 【经验】开发面向多种客户端的Server(服务端)需要考虑的问题
  • 【mySQL】mySQL动态语句(SQL语句中有变量)
  • 【机器学习】机器学习常用「线性代数」知识速查手册
  • 【SQL】SQL 注入攻击和预防
  • 【linux内核】linux内核常见考核题(30道内核考题和答案)--编辑中
  • 【linux】linux下多种锁的比较 和 实现单实例的方法
  • 【C++】C++中的回调函数——指向类成员的指针
  • 【mySQL】MYSQL数据库时间字段INT,TIMESTAMP,DATETIME性能效率比较
  • 【智慧工厂】一篇让你搞明白ERP、SAP、MES的文章
  • 收藏网友的 源程序下载网
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • If…else
  • Spring Cloud Feign的两种使用姿势
  • Theano - 导数
  • Zepto.js源码学习之二
  • 阿里研究院入选中国企业智库系统影响力榜
  • 从重复到重用
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 消息队列系列二(IOT中消息队列的应用)
  • 一、python与pycharm的安装
  • 一个普通的 5 年iOS开发者的自我总结,以及5年开发经历和感想!
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​力扣解法汇总946-验证栈序列
  • ​什么是bug?bug的源头在哪里?
  • #include到底该写在哪
  • #Linux(帮助手册)
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (差分)胡桃爱原石
  • (二十四)Flask之flask-session组件
  • (四) Graphivz 颜色选择
  • (转)Unity3DUnity3D在android下调试
  • ./configure,make,make install的作用(转)
  • .Net core 6.0 升8.0
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .NET 回调、接口回调、 委托
  • .net实现头像缩放截取功能 -----转载自accp教程网
  • @Bean有哪些属性
  • @KafkaListener注解详解(一)| 常用参数详解
  • @Transactional 详解
  • [20150629]简单的加密连接.txt
  • [android] 手机卫士黑名单功能(ListView优化)
  • [Android]Android开发入门之HelloWorld
  • [AUTOSAR][诊断管理][ECU][$37] 请求退出传输。终止数据传输的(上传/下载)
  • [BUG] Authentication Error
  • [C#基础知识系列]专题十七:深入理解动态类型