简介
Protocol Buffers就是一种google定义的结构化数据格式,用于数据的序列化和反序列化。由于它直接对二进制源数据进行操作,所以它相对于xml来说,足够的小,快以及简单,而且又与语言、平台无关,所以兼容性也有不错的表现。目前很适合做数据存储或 网络通讯间的数据传输。
操作
首先需要激活protoc命令,使用brew install protobuf安装
定义.proto文件“login.proto”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19syntax = "proto3";
package msg;
option java_package = "com.example.msg";
option java_outer_classname = "LoginMsg";
message Login{
string username = 1;
int32 pw = 2;
}编译
protoc -I=. --java_out=. login.proto
生成了LoginMsg.java
对LoginMsg进行操作
通过.proto定义生成的LoginMsg.java,已经整合了对LoginMsg的序列化和反序列化相关代码,我们对login这个消息的reader和writer时只需要通过对该class进行操作即可。比如要把loginMsg写入到流里面发送出去,只需要对loginMsg进行赋值然后writer,对象就被序列化为二进制数据写出,或者接收端读取LoginMsg时,调用其ParserbyReader,就可以基于二进制流反序列化为LoginMsg对象。
write:
read:
只需要对上述的stream改造为为socket就可以基于tcp进行消息传输了。
LoginMsg内容
Login
消息结构对象的主体,主要存储数据,同时继承GeneratedMessageV3,内部封装对象的序列化和反序列化,writeTo序列化,paser反序列化。
LoginOrBuilder
接口,用来连接Login和Builder,提供类型信息以及对外提供field get方法。
Builder
消息对象构建器,对外封装field set方法。
Descriptor
消息对象元数据的描述信息,一般用不到,如果你有动态解析的需求可以通过此来处理
Parser
解析器,为消息反序列化提供服务
结构关系如下:
MessageLite/Message接口是所有message的抽象接口,message可以基于Parser从字节流数据中构建对象,也可以通过Builder创建的对象序列化后写入字节流数据到IO管道,MessageLite和Message内部都定义了自己的Builder类,继承自MessageLiteOrBuilder以及MessageOrBuiler,并定义了MessageLite/Message和它们各自Builder类的共同接口。
调用时序
。。。
内容搬运https://www.jianshu.com/p/9cb9fb05431a
https://www.jianshu.com/p/ec39f79c0412
JavaScript版本
1 | syntax = "proto3"; |
执行protoc -I=. --js_out=. login.proto
即可以产生js版本的代码
1 | /** |
代码结构要简单的多,主要调用的是jspb库。
很容易看出来,fieldNumber很重要,所以在前后端更新协议的时候,往后面加,而不是在中间插
message的二进制结构
每个消息字段读取的时候,都会先调用一次readTag或者writeTag。tag等于就是这个value信息的描述或者定义,告知解析器当前fields是什么类型字段,以及读取的顺序,有了这个信息,解析器就知道一个field在流中的开始位置和结束位置,如此一个field解码成功,并且与字段顺序无关。
tag的构成
(fieldNumber << 3) | wireType
Protobuf的优点
fieldNumber 为每个field定义一个编号,其一保证不重复,其二保证其在流中的位置。如若当前数据流中有某个字段,而解析方没有相关的解析代码,解析放会直接skip 这个field,而且读数据的position也会后移,保证后续读取不出问题。
cautions
proto文件要使用UTF-8