protobuf入门

写在前面:
现在国内博客风气简直烂的很,百度半天七八篇protobuf相关文章一模一样。只好去挂梯子看官方文档,结果发现国内的博文就只是把官网文档翻译了一遍,一个字都没改。
就无语。。。

protobuf是什么

首先我们来看看protobuf的缔造者Google对其的一个定义:

google:
Protocol buffers are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.
摘自:protocol buffers

主旨就是,protobuf是一种类似于XML的序列化协议,无关平台、语言、机器。但是其比XML更小、更快、更简单。

小声说一句:我觉得更小更快我没意见,但是说简单,还是json简单。

protobuf的安装

这里直接借用一个博主的博客:protobuf 安装
注意,这里还需要安装一个protobuf compiler,Ubuntu直接使用以下命令安装:

sudo apt-get install protobuf compiler

protobuf 跨平台跨语言的秘密

xxxx.proto文件

安装完protobuf,我们就能解开为什么说protobuf能够跨平台跨语言了。

protobuf安装中我们会安装一个protobuf compiler,这个是proto文件的编译器,而proto文件里面写入的就是消息传输的格式。

下面举一个proto文件的例子

//test.proto文件
syntax = "proto2";

package tutorial;

message PersonInfor
{
	optional string name;	
	optional int32 age;
	required string sex;
	repeated string friends;
}
  • syntax:表示当前使用的版本是proto2,还有proto3,想了解可以点这里:传送门
  • package:java中的语法,可以理解为C++中的namespace
  • message:这个可以类比与C++中的struct关键字,里面的都是它的变量,是没有方法的
  • optional:属性修饰词,表示该属性可以设置其值,也可以不设置其值
  • required:属性修饰词,表示该属性必须有
  • repeated:属性修饰词,表示该属性是一个动态数组,可以理解为vector

当然这是在proto2语法下,在proto3语法下所有的optional和required限定就省略了,缺省条件下默认是optional。

编译xxxx.proto文件

使用以下命令编译test.proto文件:

	/*
	* 运行protoc 来生成c++文件 
	* 如果是头文件,后缀为xxxx.pb.h
	* 如果是源文件,后缀为xxxx.pb.cc
	*/
	protoc ./test.proto --cpp_out=./

这个命令会在当前路径下生成test.pb.cctest.pb.h文件。

这两个文件里面,protoc会给各个字段自动相应的方法,比如:

void set_name(const string& input);
string& get_name() const;

这都是protoc编译器自己实现的,感兴趣的朋友可以去看看这两个文件。

除了这些针对属性的函数,protoc还实现了以下四个有关序列化和反序列化的函数:

	bool SerializeToString(string* output) const;   //序列化消息,将字节以string方式输出
	bool ParseFromString(const string& data);   //解析给定的string 
	bool SerializeToOstream(ostream* output) const; //写消息给cc++ ostream中
	bool ParseFromIstreamstream(istream* input);  //从给定的c++ istream中解析出消息

经过序列化编码之后,我只需要把test.proto文件传输给对端,然后对端根据不同语言去protoc生成针对这个PersonInfo的不同语言版本函数,去处理我收到的数据。

使用protobuf,我们就可以服务端用C++写,客户端用Java写。

序列化 Demo

#include <iostream>
#include "test.pb.h"

int main()
{
	PersonInfo personinfo;
	personinfo.set_name("zhangsan");
	cout<<"name: "<<personinfo.name()<<endl;

	string serialize;
	personinfo.SerializeTostring(&serialize);
	cout<<"after serialize: "<<serialize<<endl;
	
	PersonInfo unserialize;
	unserialize.ParseFromString(serialize);
	cout<<"after unserialize: "<<unserialize.name()<<end;
}

protobuf更快的秘密

网上一直有一种说法,protobuf 比 json,比XML更快。这仿佛是被大众所认可的一种规则,但是很少有人去深究为什么?

原因在于其编码!!

json是文本传输,而protobuf虽然也有文本传输,但是其更多的来说是按编码来传输,其编码的方式是它更小,在相同的带宽下,无疑是体积更小的文件传输的越快!
**加粗样式**
图源:bilibili 花花酱

protobuf 的编码

google:
message Test1 { optional int32 a = 1; }
In an application, you create a Test1 message and set a to 150. You then serialize the message to an output stream. If you were able to examine the encoded message, you’d see three bytes:
08 96 01

???

按照我们的认知里面,对于一个整数来说,它在32位系统中表示为0x0000012c跟这三个字节完全不相关。

google官方给的解释是:
对于150,其二进制表示为0000 0000 0000 0000 0000 0000 1001 0110,可以看到基本除了最后一个字节,其他三个字节完全是用不上的,是一种对空间的浪费。所以就需要使用编码,它这里使用的是Varint编码技术。

先看一张关于protobuf类型的表:
在这里插入图片描述
图源:google protobufs

这里我们可以看到,对于int32类型,其type为0,而a的键又为1,1在二进制表示0000 0001,而varint 对于类型的编码规则为:(field_number << 3) | wire_type

代入到公式中:就是0x08

0x960x01又是什么意思呢?
先来看0x96 的二进制表示:1011 0000,01的二进制表示0000 0001
对于第一位,我们舍弃:011 0000000 0001
000 0001,可以说,它前面的7个零都是没有用的,所以舍弃!
然后和96的二进制进行组合,注意这里它们两个的要颠倒一下顺序:1 011 0000

是不是150!!!

再来看一个字符串的例子:

google:
message Test2 { optional string b = 2; }
Setting the value of b to “testing” gives you:
12 07 [74 65 73 74 69 6e 67]

首先先看第一个0x12,根据公式:(field_number << 3) | wire_type
2的二进制表示 0000 0010,根据公式计算出来 0001 0010,就是0x12
0x07表示字符串长度为7;
0x74为例,其为一个字节的char类型,其二进制表示为111 0100,直接就是 t 的ascii码表示

参考文献

[1] protobuf编译、安装和简单使用C++ (Windows+VS平台)  https://www.cnblogs.com/WindSun/p/12543066.html
[2] Protocol Buffers  https://developers.google.com/protocol-buffers/docs/encoding
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenmingik

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值