protobuf序列化协议python教程 - Go语言中文社区

protobuf序列化协议python教程


全栈工程师开发手册 (作者:栾鹏)
架构系列文章

Protobuf?

  1. 是什么?
      Google Protocol Buffer(简称 Protobuf)是一种轻便高效的结构化数据存储格式,平台无关、语言无关、可扩展,可用于通讯协议和数据存储等领域。

  2. 为什么要用?
      - 平台无关,语言无关,可扩展;
      - 提供了友好的动态库,使用简单;
      - 解析速度快,比对应的XML快约20-100倍;
      - 序列化数据非常简洁、紧凑,与XML相比,其序列化之后的数据量约为1/3到1/10。

首先我们要知道protobuf是如何使用的.我们需要先编写一个.proto的文件,这个文件用来定义数据格式,和json文件一样.下一步,我们需要proto的编译器将这个文件编译成py文件,或者h/cpp文件.最后我们在通过proto提供的python的api接口访问生成的py文件.

定义.proto文件

首先需要一个proto文件,其中定义了我们程序中需要处理的结构化数据:下面是一个.proto文件的示例(按照protobuf3的语法格式书写的)

/**
 * 测试数据格式
 */

syntax = "proto3";                 //proto3必须在文件首页加这一句

package people;                   //包名,相当于定义了python文件的名称

message Person {                    //自定义类
  string name = 1;         // 类型,变量,分配标识号
  int32 id = 2;             //类型,变量,分配标识号
  string email = 3;

  enum PhoneType {                   // 设置枚举类型
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {                         //设置子类型
    string number = 1;      // 类型,变量,分配标识号
    PhoneType type = 2;     //  类型,变量,分配标识号
  }

  repeated PhoneNumber phones = 4;               // 设置可重复类型,也就是列表
}

message AddressBook {             // 自定义类
    repeated Person people = 1;   //定义列表
}

文件的第一行指定了你正在使用proto3语法:如果你没有指定这个,编译器会使用proto2。这个指定语法行必须是文件的非空非注释的第一个行。

向.proto文件添加注释,可以使用C/C++/Java风格的双斜杠(//) 语法格式

protobuf3语法可以参考https://blog.csdn.net/u011518120/article/details/54604615

Protobuf2的语法

这里写图片描述

1 *.proto文件中数据类型可以分为两大类:

复合数据类型 + 标准数据类型

复合数据类型包括:枚举和message类型

标准数据类型包含:整型,浮点,字符串等

2 数据类型前面修饰词:

①required: 必须赋值,不能为空,否则该条message会被认为是“uninitialized”。build一个“uninitialized” message会抛出一个RuntimeException异常,解析一条“uninitialized” message会抛出一条IOException异常。除此之外,“required”字段跟“optional”字段并无差别。

②optional:字段可以赋值,也可以不赋值。假如没有赋值的话,会被赋上默认值。

③repeated: 该字段可以重复任意次数,包括0次。重复数据的顺序将会保存在protocol buffer中,将这个字段想象成一个可以自动设置size的数组就可以了。

3.每个字段要给数字:

该Number是用来标记该字段在序列化后的二进制数据中所在的field,每个字段的Number在message内部都是独一无二的。也不能进行改变,否则数据就不能正确的解包。

4 数据类型

数据类型这里可以去看Protobuf支持的基本数据类型的表

5 默认值

当一个消息被解析的时候,如果被编码的信息不包含一个特定的singular元素,被解析的对象锁对应的域被设置位一个默认值,对于不同类型指定如下:

对于strings,默认是一个空string
对于bytes,默认是一个空的bytes
对于bools,默认是false
对于数值类型,默认是0
对于枚举,默认是第一个定义的枚举值,必须为0;
对于消息类型(message),域没有被设置,确切的消息是根据语言确定的,详见generated code guide

对于可重复域的默认值是空(通常情况下是对应语言中空列表)。

注:对于标量消息域,一旦消息被解析,就无法判断域释放被设置为默认值(例如,例如boolean值是否被设置为false)还是根本没有被设置。你应该在定义你的消息类型时非常注意。例如,比如你不应该定义boolean的默认值false作为任何行为的触发方式。也应该注意如果一个标量消息域被设置为标志位,这个值不应该被序列化传输。

Proto3的语法变化

protobuf3的语法相较于protobuf2的语法变化较大

(1)语法标记

这个版本的protoc的protobuf编译器已经可以支持proto2语法和proto3的语法

如果你的proto文件没有添加syntax说明的话, 用这个版本的编译器会报错, 提示你默认proto2支持, 请添加语法标记

syntax = “proto2”;

(2)optional不需要了

只保留repeated标记数组类型, optional和required都被去掉了

实际使用证明, required的设计确实是蛋疼, C++的调试版会弹出assert,release版和optional也没啥区别

(3)map支持

map编写格式为

map<key_type, value_type> map_field = N;

例如:

map<string, Project> projects = 3;

代码生成确认支持map, 这对于很多语言来说又可以偷懒了

(4)字段default标记不能使用了
位于proto2语法的字段number后的[default=XX]

这个东西不能用了, 理由是:

对于同一段序列化后的数据, 如果序列化端的default和反序列化端的default描述不一样会导致最终结果完全不一致

即: 同一个数据两个结果, 这是不可预测的结果, 因此干掉这个特性

不过本人觉得, 对于游戏来说, 这个功能本身可以压缩很多数据,虽然会有隐患

(5)枚举默认值一定是0

proto2里的默认值是枚举的第一个value对应的值, 不一定为0

proto3在你定义value时, 强制要求第一个值必须为0

这个修改为避免隐患还是有帮助的

(6)泛型描述支持

any类型, 可以代表任何类型, 可以先读进来, 再进行解析, 没具体用, 步子跨大了怕扯到蛋

(7)支持json序列化

这个极好, json再次被同化了

(8)增加了多种语言支持

js, objc, ruby, C#等等

然而, C#版本的基础runtime库是用C# 6.0的语法写的,这对于Unity mono祖传2.0来说, 确实扯到蛋了,没法用

(9)Protobuf现在使用CMAKE做配置系统

编译起来稍微麻烦, 还要下个被墙掉的cmake…

定义服务(Service)

如果想要将消息类型用在RPC(远程方法调用)系统中,可以在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根。如,想要定义一个RPC服务并具有一个方法,该方法能够接收 SearchRequest并返回一个SearchResponse,此时可以在.proto文件中进行如下定义:

参考:https://blog.csdn.net/u014066037/article/details/72845802

编译.proto文件产出Python代码(生成xxxx_pb2.py)

我们要安装proto编译工具,能将proto文件编译成py文件或h/cpp文件.

编译工具直接在github上下载即可,地址: https://github.com/google/protobuf/releases

window下编译proto文件生成py文件

利用protoc.exe编译proto文件,cmd切换到当前目录,执行以下命令:

protoc -I=. --python_out=./ people.proto

或者直接默认在当前文件夹下寻找
 protoc --python_out=./ people.proto

-I=源文件目录,–python_out=编译生成的文件的路径 ,people.proto是要编译的协议文件

编译好之后你就会在目标目录里面看到输出的结果文件,如下:people_pb2.py

在ubuntu下编译proto文件生成py文件

ubuntu安装protobuf编译器

sudo apt install protobuf-compiler

不过通过官网安装的并不一定是最新版本的.所以建议还是通过github下载安装.

我这里现在的是protobuf-python-3.6.0.tar.gz

tar -zxvf protobuf-3.6.0.tar.gz
cd protobuf-3.6.0/
./configure
make
make check
sudo make install
protoc --version      检查是否安装成功

最后我安装完成,用上述命令检查版本号时出现如下问题

protoc: error while loading shared libraries: libprotocbuf.so.16: cannot open shared

错误原因

protobuf的默认安装路径是/usr/local/lib,而/usr/local/lib不在ubuntu体系默认的LD_LIBRARY_PATH里,所以就找不到lib

解决办法

1 在 /etc/ld.so.conf.d/目录下创建文件 bprotobuf.conf文件,文件内容如下

/usr/local/lib

2 输入命令

sudo ldconfig

这时,再输入protoc --version就可以正常看到版本号了

使用protobuf编译器编译proto文件.进入你的proto文件所在的目录.

protoc -I=. --python_out=. people.proto

表示将当前目录下的people.proto文件编译成py文件.

当然如果你不是使用python开发,你也可以讲proto编译成其他的语言所需要的格式,如成c++语言的h文件和cpp文件.

python调用protobuf生成的py模块

我们要先安装protobuf库

pip install protobuf

在自己的python文件中引入这个数据进行调用

from people_pb2 import Person

alluser=[]

def write_test():
    person = Person()
    person.name='name1'
    person.id=1
    person.email='111@aa.com'
    alluser.append(person)


def read_test():
    print(alluser[0])


if __name__ == "__main__":
    write_test()
    read_test()


# 创建对象
item = arctern_pb2.Item()   #Item为proto文件中创建的类型
# 将对象转化为字符串
str_data= item.SerializeToString()
# 将字符串转化为对象
item.ParseFromString(str_data)   # str_data必须为同类proto对象序列化以后的字符串
#遍历proto中定义的可重复对象
for item in arr:  # arr为proto中定义的repeated对象
	pass
# 以下标索引的方式读取可重复元素
item = arr[0]
# 可重复对象添加元素
item1=arr.add()  # 为arr对象添加一个元素,并返回元素的索引, arr为proto中定义的repeated对象
arr.append(item2)  # 将一个元素添加到arr中,arr为proto中定义的repeated对象
# 将一个实例对象的数据 复制给另一个对象
item1.MergeFrom(item2)  # item1和item2必须是相同类型的
# 对象清除某个属性
item.ClearField('attr1')



对于map元素,例如在proto中
message HelloRequired {
    map<string, string> args = 1;
}    


在python中
import hellorequired_pb2
test = hellorequired_pb2.HelloRequired()
test.args["key1"] = "value1"
test.args["key2"] = "value2"

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/luanpeng825485697/article/details/81029492
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢