Protobuf(Protocol Buffers)安装及 Python、C# 使用示例

官网地址:https://developers.google.cn/protocol-buffers

GitHub 地址:https://github.com/protocolbuffers/protobuf

优点:

1、性能好

2、跨语言

缺点:

1、二进制格式可读性差:为了提高性能,protobuf 采用了二进制格式进行编码,这直接导致了可读性差。

2、缺乏自描述:XML 是自描述的,而 protobuf 不是,不配合定义的结构体是看不出来什么作用的。

Windows 下安装:

下载地址:https://github.com/protocolbuffers/protobuf/releases

可直接下载:protoc-3.13.0-win64.zip,这个是编译后的压缩包,免安装,将其下的 bin 目录添加到环境变量就可以了。

        打开命令提示符,输入命令:protoc --version,成功显示版本号,则表示安装成功。

使用 protobuf 首先需要定义 .proto 文件,定义 Person.proto 文件内容如下

syntax = "proto3";

package Test;

message Person {

  string Name = 1;

  int32 Age = 2;

  bool Marriage = 3;

}

syntax = "proto3":指定正在使用 proto3 语法,否则 protobuf 将默认使用的是 proto2。

package Test:指定命名空间(C# 中)。

message:是关键字,定义结构化数据。

注:等号后面的数字是字段唯一编号(注意不是字段的值),用于二进制格式消息中标识字段。

编译命令:

    protoc -I=D:\protoc-3.13.0-win64\bin --csharp_out=D:\protoc-3.13.0-win64\bin Person.proto

命令说明:

protoc:是 protobuf 自带的编译器,可以将 .proto 文件编译成 java、python、go、C# 等多种语言的代码,直接引用。

-I:表示源文件(.proto 文件)所在文件夹路径。

--python_out:表示目标语言为 python,且指定生成的 .py 文件存放目录。相应的,C# 为 csharp_out。

Person.proto:为源文件文件名,如果有多个,空格隔开。

Python 示例

调用编译命令编译 Person.proto,编译后生成文件:Person_pb2.py,添加至项目中,序列化和反序列化示例如下:

import Person_pb2

person = Person_pb2.Person()

person.Name = '张三'

person.Age = 20

person.Marriage = True

# 序列化

b = person.SerializeToString()

print(b)

# 反序列化

p = Person_pb2.Person()

p.ParseFromString(b)

# p = Person_pb2.Person().ParseFromString(b) # 注意,不能这样写,这是错误的。

print(f'Name: {p.Name}; Age: {p.Age}; Marriage: {p.Marriage}')

输出:

b'\n\x06\xe5\xbc\xa0\xe4\xb8\x89\x10\x14\x18\x01'

Name: 张三; Age: 20; Marriage: True

C# 示例

C# 下的 Protobuf 有 3 个版本:

Google.ProtoBuf:Google官方版本,https://github.com/protocolbuffers/protobuf/tree/master/csharp

protobuf-net:.net 社区版本,由 .net 社区爱好者开发,https://github.com/protobuf-net/protobuf-net

Google.ProtocolBuffers:据说是由谷歌的 .net 员工在官方版本还未出来的时候开发的,https://github.com/jskeet/protobuf-csharp-port

这里介绍谷歌官方版本,在 VS 中,通过 NuGet 安装 'google.protobuf' 包。

using Google.Protobuf;

using System;

using Test;

namespace Protobuf

{

    class Program

    {

        static void Main(string[] args)

        {

            Person person = new Person();

            person.Name = "张三";

            person.Age = 20;

            person.Marriage = true;

            // 序列化

            byte[] buffer = person.ToByteArray();

            foreach (byte b in buffer)

            {

                Console.Write(b.ToString("X2") + " ");

            }

            Console.WriteLine();

            // 反序列化

            Person p = Person.Parser.ParseFrom(buffer);

            Console.WriteLine(string.Format("Name: {0}, Age: {1}, Marriage: {2}", p.Name, p.Age, p.Marriage));

            Console.Read();

        }

    }

}

输出:

0A 06 E5 BC A0 E4 B8 89 10 14 18 01

Name: 张三, Age: 20, Marriage: True

比较一下 Python 的输出,好像不一样,Python 中第一个字节是 \n,而这里是 0A。\n 在 ASCII 中的值就是 0A。所以两种语言的序列化结果是一样的。