最近在学习蘑菇街的TeamTalk开源代码,新版的代码中包含了最新的很多框架和技术,其中包括了谷歌的Protocol Buffer简称ProtoBuf,该篇文章是我通过阅读谷歌开发者中心后的学习总结,顺便吐槽下,国内的网络真是蛋疼,现在家庭宽带或者有些单位的宽带连谷歌开发者中心都访问不了,更别谈什么facebook啥的了,我能访问谷歌开发者中心是通过一个VPN工具LoCo加速器,虽然是付费的,但是一不贵也就几十块钱一个月,速度和稳定性都不错,用来访问网页足够了。
闲话不多说,进入正题,需要详细了解ProtoBuf的童鞋可以自己访问谷歌开发者中心:https://developers.google.com/protocol-buffers/
简介
ProtoBuf是一种灵活高效的独立于语言平台的结构化数据表示方法,可用于表示通信协议和数据存储等各方面,与XML相比,ProtoBuF更小更快更简单。你可以用定义自己ProtoBuf的数据结构,用ProtoBuf编译器生成特定语言的源代码,(如C++,Java,Python等,目前ProtoBuf对主流的编程语言都提供了支持)方便的进行序列化和反序列化。
ProtoBuf 工作机制
message Person { required string name = 1; required int32 id = 2; 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; }.pro文件的每一个属性都是一个包含类型和名称的对,protobuf的类型可以是string,int,bool,或者是其他ProtoBuf源文件中定位的类型等。还开元指定该属性的一些特殊字段,比l如optional表示该属性是可选的,required表示该属性是必须的,repeated表示该属性是一个list的集合(不知道用list类似形容是否合适,就差不多意思吧,包含1个或多个元素),包含了若干个相同类型的值,不过在protoBuf 3中,optional和required都不需要的了,如果配了两个,protoBuf 编译器还会报错,但是repeated的还是保留的,用来说明该字段是一个list。
Person person; person.set_name("John Doe"); person.set_id(1234); person.set_email("[email protected]"); fstream output("myfile", ios::out | ios::binary); person.SerializeToOstream(&output);
可以像如下方式反序列化,读取各个字段
fstream input("myfile", ios::in | ios::binary); Person person; person.ParseFromIstream(&input); cout << "Name: " << person.name() << endl; cout << "E-mail: " << person.email() << endl;
ProtoBuf的一个亮点是提供了向前和向后的兼容性,你可以再你定义的消息格式中增加字段或者删除字段,当消息由一个增加字段后的系统向一个老版本系统发送消息时,老版本解析该消息格式后会直接忽略新增的字段,而当消息从老版本发向新版本时,新版本解析该消息时会将新增的字段设置为默认值,所以你不需要担心在系统中增加消息字段后的兼容问题以及版本升级配套问题。
为什么不用XML
<person> <name>John Doe</name> <email>[email protected]</email> </person>
而在ProtoBuf中的定义是这样的:
# Textual representation of a protocol buffer. # This is *not* the binary format used on the wire. person { name: "John Doe" email: "[email protected]" }
cout << "Name: " << person.name() << endl; cout << "E-mail: " << person.email() << endl;
而XML将会做如下动作:
cout << "Name: " << person.getElementsByTagName("name")->item(0)->innerText() << endl; cout << "E-mail: " << person.getElementsByTagName("email")->item(0)->innerText() << endl;
然而,ProtoBuf与XML相比,有某些用途下并不是一个很好的解决方案,如ProtoBuf,并不适合用于给基本标签的文本文档建模,此外XML是易读的,自描述的,而ProtoBuf的二进制文档并不是自描述的,除非拥有数据的源文件,否则你很难理解序列化后的文件中的内容,它是一个二进制文件。