protobuf的使用和原理

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式,说白了其实就是用来传输数据的,本文介绍用Java实现的protocolBuffer是如何传输数据的
protobuf的编译器和运行时的java环境可以去官网或者这里去下载http://download.csdn.net/detail/u010031673/9464262 一定要保证编译器的版本和java环境的版本。
简单的protobuf例子
新建一个.proto文件,内容如下


package com.klq.net.protobuf; option java_outer_classname = "AddressBookProtos"; message Person { required string name = 1; required int32 id = 2; optional string email = 3; }

属性有三种可选类型
required:不可空字段(在序列化传输之前一定要设置值)
optional:可空字段(在序列化传输之前可以不设置值)
repeated:可重复字段,和java中的数组对应
protobuf和高级语言中数据类型的对应关系如下

【protobuf的使用和原理】每一个字段都有一个唯一标识,这个标识的作用是在格式化成二进制的消息中识别定义的每一个字段,这个标识一旦定义后就不应该再进行修改
protobuf可以为字段设置默认值,格式如下


required int32 id=2 [default=10];

我们还可以在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; }


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; }message AddressBook { repeated Person person = 1; }

编译
将上面这个文件定义为test.proto文件,放到这个目录下(可以是任意目录)
protobuf的使用和原理
文章图片

打开DOS命令行窗口进入D: 输入如下命令
protobuf的使用和原理
文章图片


会在当前目录下生成一个目录结构为包层次目录结构的文件,文件名称为.proto文件中定义的名称
protobuf的使用和原理
文章图片

新建一个maven项目(普通项目也可以),将生成的文件复制到该项目下,生成proto文件时一定要保证定义的包结构和proto文件放在项目中的位置保持一致,项目结构如下:
protobuf的使用和原理
文章图片

在pom文件中引入如下依赖,如果不是maven项目则自行引入jar包,jar包在上面的下载链接中有

com.google.protobuf protobuf-java 2.6.1

protobuf的使用和原理
文章图片

下面介绍如何在程序中使用protobuf
SendMessageDemo:


package com.klq.net.protobuf; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.sql.Savepoint; import com.klq.net.protobuf.AddressBookProtos.AddressBook; import com.klq.net.protobuf.AddressBookProtos.Person; public class SendMessageDemo { private static File addressBookFile; public static void main(String[] args) { //定义一个Person对象Person.newBuilder(),并使用一个 //引用Person.Builder来指向它 Person.Builder person = Person.newBuilder(); person.setName("Hugh Jackman"); person.setId(188); person.setEmail("10086@163.com"); //创建一个PhoneNumber内部类对象 Person.PhoneNumber.Builder phoneNumber1= Person.PhoneNumber.newBuilder(); phoneNumber1.setNumber("1008611"); phoneNumber1.setType(Person.PhoneType.MOBILE); person.addPhone(phoneNumber1); Person.PhoneNumber.Builder phoneNumber2= Person.PhoneNumber.newBuilder(); phoneNumber2.setNumber("10010"); phoneNumber2.setType(Person.PhoneType.MOBILE); person.addPhone(phoneNumber2); AddressBook.Builder addressBook= AddressBook.newBuilder(); addressBook.addPerson(person); SendMessageDemo demo = new SendMessageDemo(); demo.addressBookFile=new File("addressBook.txt"); demo.saveToFile(addressBookFile, addressBook); } private void saveToFile(File file,AddressBook.Builder addressBook){ byte[] target = addressBook.build().toByteArray(); try { OutputStream fos = new FileOutputStream(file); fos.write(target); }catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }

ReceiveMessageDemo:

package com.klq.net.protobuf; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import com.google.protobuf.InvalidProtocolBufferException; import com.klq.net.protobuf.AddressBookProtos.AddressBook; import com.klq.net.protobuf.AddressBookProtos.Person; import com.klq.net.protobuf.AddressBookProtos.Person.PhoneNumber; public class ReceiveMessageDemo { private static File addressBookFile=new File("addressBook.txt"); public static void main(String[] args) { ReceiveMessageDemo rmd = new ReceiveMessageDemo(); try { InputStream io = new FileInputStream(addressBookFile); AddressBook addressBook=AddressBookProtos.AddressBook.parseFrom(io); for(Person p:addressBook.getPersonList()){ System.out.println("name:"+p.getName()); System.out.println("id:"+p.getId()); System.out.println("email:"+p.getEmail()); for(PhoneNumber pn:p.getPhoneList()){ System.out.println("person's phoneNumer:"+pn.getNumber()); System.out.println("person's phoneNumerType:"+pn.getType()); } } //System.out.println(addressBook.toString()); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

目录结构如下:
protobuf的使用和原理
文章图片


这个例子本身并无意义,但只要稍加修改就可以将它变成更加有用的程序。比如将磁盘替换为网络 socket,那么就可以实现基于网络的数据交换任务。而存储和交换正是 Protobuf 最有效的应用领域



    推荐阅读