Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。
Protobuf和Json的比较
这就是protubuf的一些比较抽象的介绍,不过简单地说,protobuf其实就是一种数据传输的格式,事实上,我们需要知道pb这种格式用来传输数据到底会给我们带来如何的好处.我们可以拿pb格式和json做对比.其实json这种数据传输的格式大家应该非常熟悉.但是json有什么不好的呢,首先json大,json格式是字符串形式,和压缩为二进制的pb格式肯定是要来的大的.此外,json很坑,因为需要对不同的语言编写json解析的程序.......php和python自然可以比较轻松的解析,java就坑了.......而且这些解析器的性能并一致,有好有坏。
.proto文件介绍
message student{
required int64 sid = 1;//学生id
required string name = 2;//学生姓名
}
这里面我们发现:
- message类似于C里面的struct,表示我们定义的是一个消息类型.
- studeng是具体的类型名称.
- required是表示这个字段是必须赋值的.
- int64和string是该字段的类型.
- =后面的数字表示是该字段在二进制文件中的序号,name为2,表示它一定在sid的后面.
现在我们如果想要加上一个性别的字段和年龄的字段,性别的字段是enum类型.年龄类型是int32,怎喵加呢?很简单.
enum Sex{
MALE = 1;
FEMALE = 2;
}
message Student{
required int64 sid = 1;//学生id
required string name = 2;//学生姓名
optional Sex sex = 3; //学生性别
optional int32 age = 4;//学生年纪
}
easy啊.等等,现在我们又有一个消息,比如我们有个班级的消息,希望能够列出里面每个学生的这些基本信息,怎么搞呢?
首先,我们需要定义一个班级的消息.
message Banji{
required int64 cid = 1;//班级的id
required string cname = 2;//班级的编号
repeated Student students = 3;//学生
}
这里我们要注意几个点:
- required前缀表示该字段为必要字段,既在序列化和反序列化之前该字段必须已经被赋值。与此同时,在Protocol Buffer中还存在另外两个类似的关键字,optional和repeated,带有这两种限定符的消息字段则没有required字段这样的限制。
- 标签值为1到15的字段在编码时可以得到优化,既标签值和类型信息仅占有一个byte.标签范围是16到2047的将占有两个bytes,而ProtocolBuffer可以支持的字段数量则为2的29次方减一。有鉴于此,我们在设计消息结构时,可以尽可能考虑让repeated类型的字段标签位于1到15之间,这样便可以有效的节省编码后的字节数量。
- 在每个消息中必须至少留有一个required类型的字段.
- 如果打算在原有消息协议中添加新的字段,同时还要保证老版本的程序能够正常读取或写入,那么对于新添加的字段必须是optional或repeated。道理非常简单,老版本程序无法读取或写入新增的required限定符的字段。
- 在原有的消息中,不能移除已经存在的required字段,optional和repeated类型的字段可以被移除,但是他们之前使用的标签号必须被保留,不能被新的字段重用。
- int32、uint32、int64、uint64和bool等类型之间是兼容的,sint32和sint64是兼容的,string和bytes是兼容的,fixed32和sfixed32,以及fixed64和sfixed64之间是兼容的,这意味着如果想修改原有字段的类型时,为了保证兼容性,只能将其修改为与其原有类型兼容的类型,否则就将打破新老消息格式的兼容性。
这里还有一个问题,Banji这个消息要用到Student的定义,我们可以回忆一下java中的处理方式,这种互相定义之间的依赖可以用package和import来解决.这样,我们可以给出完整的.proto的定义方式:
test.student.proto
package test;
option java_package = "com.songfy.pb";
option java_outer_classname = "StudentProtobuf";
enum Sex{
MALE = 1;
FEMALE = 2;
}
message Student{
required int64 sid = 1;//学生id
required string name = 2;//学生姓名
optional Sex sex = 3; //学生性别
optional int32 age = 4;//学生年纪
}
test.banji.proto
import "test.student.proto";
package test;
option java_package = "com.songfy.pb";
option java_outer_classname = "BanjiProtobuf";
message Banji{
required int64 cid = 1;//班级的id
required string cname = 2;//班级的编号
repeated Student students = 3;//学生
}
ok,这样我们就把我们的消息类型定义好了....那么如何写代码呢?下面我们细细道来.
编译.proto
protoc --java_out="/usr/home/feng/protos/src" test.banji.proto test.student.protoJava代码
package com.songfy.pb;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.songfy.pb.BanjiProtobuf.Banji;
import com.songfy.pb.StudentProtobuf.Sex;
import com.songfy.pb.StudentProtobuf.Student;
public class Main {
public static void main(String[] args) throws IOException {
//获取student的build sb
Student.Builder sb = Student.newBuilder();
//加入两个学生
sb.setSid(110);
sb.setName("shuaiguo");
sb.setSex(Sex.MALE);
sb.setAge(11);
List<Student> list = new ArrayList<Student>();
list.add(sb.build());
sb = Student.newBuilder();
sb.setSid(119);
sb.setName("lilei");
sb.setSex(Sex.FEMALE);
sb.setAge(20);
list.add(sb.build());
//产生一个班级
Banji.Builder bb = Banji.newBuilder();
bb.setCid(13);
bb.setCname("dafengqi");
bb.addAllStudents(list);
//刷入到文件中
FileOutputStream fos = new FileOutputStream("C:/Users/songfy/Desktop/test.protoout");
bb.build().writeTo(fos);
fos.close();
//从文件读回
Banji bb1 = Banji.parseFrom(new FileInputStream("C:/Users/songfy/Desktop/test.protoout"));
System.out.println(bb1.getCid() + "\t" + bb1.getCname());
for(Student s: bb1.getStudentsList()){
System.out.println(s.getSid() + "\t" + s.getName() + "\t" + s.getSex() + "\t" + s.getAge());
}
}
}
这就是我们代码了,似乎非常easy啊........
这样,我们可以看到输出结果为:
13 dafengqi
110 shuaiguo MALE 11
119 lilei FEMALE 20
节选自:http://www.cnblogs.com/songfy/p/4732560.html
-----------------------------------------------------
请尊重作者劳动成果,
转载请注明出处