Protobuf是Google开发一种数据描述语言,能够将结构化数据序列化,可用于数据存储、通信协议等方面。据Google官方文档介绍,现在Google内部已经有48,162个消息类型定义在12,183个proto文件中。

简介

protobuf是Google开发的一种数据描述语言语言,能够将结构化的数据序列化,可用于数据存储,通信协议等方面,官方版本支持C++,Java,Python,社区版本支持更多语言。

比如说程序中生成了一个链表,但是程序退出重启后,还要重新生成链表,有时候,我们很需要上次程序中该链表中记录的数据。这些数据或许是经过很多大量运算生成的,每次都重新生成这些数据的话,需要消耗大量时间。这时候就可以考虑使用protobuf,将其序列化后保存在文件中,下次使用的时候,加载文件,反序列化后就可以直接使用了。

该项目在github的官方地址

值得注意的是,protobuf是以二进制来存储数据的。相对于JSON和XML具有以下优点:

  1. 简洁

  2. 体积小:消息大小只需要XML的1/10 ~ 1/3

  3. 速度快:解析速度比XML快20 ~ 100倍

  4. 使用Protobuf的编译器,可以生成更容易在编程中使用的数据访问代码

  5. 更好的兼容性,Protobuf设计的一个原则就是要能够很好的支持向下或向上兼容

protobuf开发环境

主要介绍c++,Java开发环境,使用的版本protobuf 2.3.0。因为Android5.1.1源码中使用的就是该版本,为了能快速编译出Android能使用的so库,所以选择这个版本。如果喜欢最新版,可以从上面的github官网下载。

点此下载protobuf 2.3.0版本源码

ubuntu :
./configuer --prefix=指定安装路径(可以不指定)make make install (可能需要sudo权限)

会编译出一个protoc的可执行程序,以及开发所需的头文件和库。这个可执行程序,要经常使用,用来将proto协议文件,转换为头文件和对应的代码。

java:

点此进入jar包下载页面,选择2.3.0版本。供java程序调用。

Android native:

Android 源码/external/protobuf

直接:

mmm external/protobuf

会编译出j出很多jar,和c++静态库。jar,推荐还是选择从上面下载的,静态库使用不是很方便。这里修改Android.mk编译so库版本。

# C++ full library - libcxx version
# =======================================================
include $(CLEAR_VARS)LOCAL_MODULE := libprotobuf-cpp-2.3.0-full-libcxx-rtti
LOCAL_MODULE_TAGS := optional
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := $(protobuf_cc_full_src_files)
LOCAL_C_INCLUDES := \$(LOCAL_PATH)/android \external/zlib \$(LOCAL_PATH)/srcLOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)LOCAL_CFLAGS := -frtti $(IGNORED_WARNINGS)
LOCAL_CPPFLAGS := -wLOCAL_SHARED_LIBRARIES := libzinclude external/libcxx/libcxx.mkinclude $(BUILD_SHARED_LIBRARY)
#include $(BUILD_STATIC_JAVA_LIBRARY)
# Clean temp vars
protobuf_cc_full_src_files :=

这里还用到了libcxx库,该库实现了c++11很多特性,开发Android Native程序时,我经常使用到这个库,所以这里也用该库来编译protobuf了。

成功后,如下所示:

Install: out/target/product/shamu/system/lib/libprotobuf-cpp-2.3.0-full-libcxx-rtti.so
快速使用

主要是介绍如何使用,前面在PC主机中编译出来的protoc可执行程序,将proto协议文件转换为对应的代码,也顺便看下proto协议文件长什么样子。

如下片段,摘自protobuf源码中的example中的addressbook.proto

message Person {required string name = 1;required int32 id = 2;        // Unique ID number for this person.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;
}

使用protoc命令生成代码,使用­­cpp_out、­­java_out、­­python_out命令选项可以生成C++、Java、Python代码。例如:

protoc addressbook.proto -I. --cpp_out=. 

上述命令执行后,产生两个用于c++新文件:

addressbook.pb.cc
addressbook.pb.h

可以将cc改为cpp.之后在自己的工程中包含这两个文件就可以按照下面所示使用proto中定义的数据协议了:

这两个文件可以大致粗略的看看,因为可以从中知道定义哪些可用的调用方法等,一看便知怎么用。

proto文件的语法

在消息定义中,需要明确以下三点:

  1. 确定消息命名,给消息取一个有意义的名字。

  2. 指定字段的类型

  3. 定义字段的编号,在Protocol Buffers中,字段的编号非常重要,字段名仅仅是作为参考和生成代码用。需要注意的是字段的编号区间范围,其中19000 ~ 19999被Protocol Buffers作为保留字段。

先来看一个非常简单的例子。假设你想定义一个“搜索请求”的消息格式,每一个请求含有一个查询字符串、你感兴趣的查询结果所在的页数,以及每一页多少条查询结果。可以采用如下的方式来定义消息类型的.proto文件了:

message SearchRequest {required string query = 1;optional int32 page_number = 2;optional int32 result_per_page = 3;
}

SearchRequest就是消息的名字,该消息有3个字段,在消息中承载的数据分别对应于每一个字段。其中每个字段都有一个修饰符,一种类型,一个名字和一个编号。

所指定的字段类型修饰符必须是如下之一:

required:一个格式良好的消息一定要含有1个这种字段。表示该值是必须要设置的;

optional:消息格式中该字段可以有0个或1个值(不超过1个),也就是可有可无;

repeated:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。表示该值可以重复,相当于java中的List。

其中数据类型和c++,java的对应如下:

分配编号:
正如上述文件格式,在消息定义中,每个字段都有唯一的一个数字标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。

消息也是可以嵌套的,即message套message,还可以使用枚举。还可以使用import导入其他proto文件。

包(Package)

通常都要为.proto文件增加一个package声明符,用来防止不同的消息类型有命名冲突。

package foo.bar;
message Open { ... }

在其他的消息格式定义中可以使用包名+消息名的方式来定义域的类型,如:

message Foo {...required foo.bar.Open open = 1;...
}

包的声明符会根据使用语言的不同影响生成的代码。

对于C++,产生的类会被包装在C++的命名空间中,如上例中的Open会被封装在 foo::bar空间中;

对于Java,包声明符会变为java的一个包,除非在.proto文件中提供了一个明确有java_package;

对于 Python,这个包声明符是被忽略的,因为Python模块是按照其在文件系统中的位置进行组织的。

protobuf使用流程

  1. 编写协议文件,也就是.proto文件

  2. 利用protoc命令将协议文件转换为我们需要的开发语言接口.该接口中包含了对协议中定义数据的操作方法.可以从生成的.h文件中查看有哪些接口,常用的就是设置字段数据,获取字段数据,序列化,反序列化等;

    列举一些公用方法:

isInitialized(): 检查是否所有的required字段是否被赋值
toString(): 返回一个便于阅读的message表示(本来是二进制的,不可读)
byte[] toByteArray();: 序列化message并且返回一个原始字节类型的字节数组
static XXX parseFrom(byte[] data);: 将给定的字节数组解析为message
void writeTo(OutputStream output);: 将序列化后的message写入到输出流
static Person parseFrom(InputStream input);: 读入并且将输入流解析为一个message
  1. 在项目中使用生成的接口操作协议数据;

    协议中每个message都会转换为一个类,使用的时候创建一个消息对象,初始化里面的属性值并且序列化,然后可以存储到文件,可以socket发送等等.

    使用数据的时候,先反序列化,然后就可以正常使用了.

  2. c++编译如下,注意加pthread库,否则出错.

g++  xx.cpp xx.pb.cc  -I /usr/local/protobuf/include -L /usr/local/protobuf/lib -lprotobuf -pthread

protobuf编码协议

搞清楚编码协议,是为了能清楚数据大小,这样才调试socket程序的时候,能看懂数据长度的含义.

在Protobuf中采用Base­128变长编码,所谓变长编码是和定长编码相对的,定长编码使用固定字节数来表示,如int32类型的数字固定使用4 bytes表示,而变长编码是需要几个字节就使用几个字节,如对于int32类型的数字1来说,只需要1 bytes足够。Base­128变长编码的原则就两条

  1. 每个字节使用低7位表示数字,除了最后一个字节,其他字节的最高位都设置为1。

  2. 采用Little­Endian字节序

test.proto

message Book{required uint32 money = 1;
}

编译proto文件:

protoc test.proto -I. --cpp_out=.

main.cpp

#include "test.pb.h"#include <stdio.h>int main(){Book book;book.set_money(150);int size = book.ByteSize();unsigned  char bts[size];book.SerializeToArray(bts, size);for(int i=0;i<size;i++)printf("0x%x \n",bts[i]);return 0;
}
g++ main.cpp test.pb.cpp -I ~/mylib/protobuf/include -L ~/mylib/protobuf/lib -lprotobuf -o test -lpthread

运行test:

0x8
0x96
0x1 

一个Protobuf的消息包含一系列字段key/value,每个字段由一个变长32位整数作为字段头,后面跟随字段体。字段头,也就是key的格式如下:

(field_number << 3) | wire_type
­field_number:   字段序号
­wire_type:  字段编码类型

字段编码类型如下:

字段序号,就是在定义message中的字段的时候添加的标号.

再看一个例子:

看嵌套:

message Test1{
requaried int32 a = 1;
}
message Test3{
requaried Test1 c = 3;
}

负数编码:

采用ZigZag Encoding

Protobuf简单使用相关推荐

  1. linux加protobuf变量环境,protobuf简单介绍和ubuntu 16.04环境下安装教程

    protobuf简单介绍 protobuf是谷歌的开源序列化协议框架,结构类似于XML,JSON这种,显著的特点是二进制的,效率高,主要用于通信协议和数据存储等方面,算是一种结构化数据的表示方法. p ...

  2. Protobuf简单编写与使用

    Protobuf简单介绍 首先Protobuf是google公司提出的一种序列化方法,因为其在网络传输中优点较多,同时使用简单方便维护,因此现如今Protobuf以及逐渐取代Json来传输数据. Pr ...

  3. protobuf简单序列化反序列化示例

    protoc命令格式 protoc    -I=SRC -cpp_out = DRC SRC\*.proto SRC:源路径: DRC:目的路径: 当出现下面无法打开文件错误时,应在工程属性目录下的包 ...

  4. Unity Protobuf 简单 案例 详解 从下载到安装和使用

    Protobuf介绍 Protocol Buffers是Google的一种数据交换的格式,一种轻量&高效的结构化数据存储格式.可以用于结构化数据串行化(序列化).由于它是二进制的格式,比使用x ...

  5. ProtoBuf 简单测试

  6. 最常用的两种C++序列化方案的使用心得(protobuf和boost serialization)

    From: http://www.cnblogs.com/lanxuezaipiao/p/3703988.html 导读 1. 什么是序列化? 2. 为什么要序列化?好处在哪里? 3. C++对象序列 ...

  7. protobuf2和3同时安装_在 Ubuntu 上安装 Protobuf 3 的教程详解

    什么时候需要安装 如果使用 protoc 命令,遇到 Protoc not found,表示未安装.或者,执行时出现错误:This parser only recognizes "proto ...

  8. C++ 序列化和反序列化学习

    定义 程序员在编写应用程序的时候往往需要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯.这些过程将会涉及到程序数据转化成能被存储并传输的格式,因此被称 ...

  9. 在Unity3D中使用Protobuf3

    Protobuf:平台无关.语言无关.可扩展且轻便高效的序列化数据结构的协议,一般用于网络通信和数据存储 第一步:下载并准备DLL文件 链接:mvn9(或者 https://github.com/pr ...

最新文章

  1. QT之Variant
  2. Manual手册的正确姿势
  3. 【华为云技术分享】LiteOS无法直接烧录或者烧录失败解决方法--ST-Link
  4. React15.6.0实现Modal弹层组件
  5. CSS 元素超出部分滚动, 并隐藏滚动条
  6. 一种提升语音识别准确率的方法与流程
  7. SQL Server中以星期一为每周第一天 计算周数
  8. 超级简单好用的免费CRM客户管理软件,推荐!
  9. 用计算机弹起风了歌词,《起风了》歌词
  10. 硬盘变成Raw格式 与 移动硬盘报I/O错误问题
  11. 《逆商:我们该如何应对坏事件》201903
  12. ts定义数组类型_ts基本类型
  13. The Auto-extending innodb_system data file './ibdata1' is of a different size 6400 pages (rounded do
  14. 区块链行业缺乏统一标准,成为金融新基建尚存距离
  15. 【算法】只有五行的Floyd最短路算法
  16. h5--块级元素与行内元素
  17. 2021年小红书品牌经典爆文拆解
  18. 10年,知乎为何依旧“赚钱难”?
  19. [数据分析实例5]使用python-pandas对历届世界杯数据进行数据分析,并用matplotlib绘图,干货满满,赶紧收藏学习起来!
  20. 第三章 VB的程序设计基础

热门文章

  1. 强化学习经典应用环境-Atari 2600游戏合集
  2. ESP32 WIFI MESH 网络个人学习笔记整理
  3. mvp的全称_库里常规赛mvp是哪一年,分别于2014-15和2015-16赛季荣膺
  4. 这款必应出品的壁纸软件也太棒了
  5. Crash自动修复系统
  6. vs2015启动无法工作,问题事件名称: APPCRASH,故障模块名称: KERNELBASE.dll
  7. oracle根据关键字搜索存储过程
  8. ubuntu 改屏幕分辨率命令_ubuntu下修改分辨率
  9. FoodDelivered-Robot---送餐机器人(六)模块驱动代码---IO采集部分
  10. win10制作软盘,xp下进行编译,最后回到win10运行