其实前面的前面的一篇文章写过关于protocol buffer的。但是不够详细,现在从最基本的开始写起。关于protocol buffer就不多做介绍了。直接进入主题,写再多的理论语言大家也不一定能懂也,最好的方法就是和一些简单的代码例子一起说明。本系列都用Java语言编写。
首先的准备工作就是需要下载相关的组件:protobuf-java-2.5.0.jar是protobuf的jar包,protoc-2.5.0-win32中的protoc.exe就是protobuf在Windows下代码生成的工具。PS:两者的版本号需要相同。下载地址:http://download.youkuaiyun.com/download/u014489596/7938485
接下来就是编写proto文件自定义消息。下面是一个最简单的TestSimpleProtoBuf.proto的定义
package client.message;
option java_package = "client.message";
option java_outer_classname = "SimpleProtoBuf";
message FigureInfo {
//ID不可为空
required int32 ID = 1;
//Name不可为空
required string Name = 2;
//可以不空也可以空
optional string address = 3;
//可以不空也可以空,可以出现任意次
repeated string figureInfos = 4;
}
第一行就是表明将此文件打包,package后面的名字随便写,为了明了,命名和项目相关。第二行就是打包的包名(ps:像我这样写的包名的话,若java_out目录下面没有client包的话那就是创建的包名是client.message,如果已经有client包的话,就是在client里面生成message包,产生嵌套关系)。第三行就是将此proto文件生成Java代码后的类名(若不写,默认使用proto的文件名作为类型,符合Java类名规范)。前三行可以不写,但是最好不要省去,方便别人阅读。
消息数据结构的定义都是message XXX,里面的每一个字段都有名称和类型,名称有三种修饰符:required、optional和repeated。required表明此字段是不可少的,不能为空。optional可以不给值。repeated表明此项可以任意重复多次。等号后面的数字是唯一标识符。
接下来就是将proto文件生成Java代码。将TestSimpleProtoBuf.proto文件和protoc.exe文件放在同一目录下,cmd进入到此目录,执行如下指令:protoc.exe --java_out=D:\webgame_workspace\TestProtobufSimple\srcTestSimpleProtoBuf.proto。java_out=后面的就是你想生成Java文件的目录,我们直接放到测试工程项目的src目录下(新建的Java项目需要引入proto的jar架包)。其实这样比较麻烦,好的方法是写一个脚本处理,前面的protobuff 简单入门文章有介绍,这里就不说了。
然后就是简单测试一下。由于我们的东西都简单,我们将序列化和反序列化的过程都放在同一个Java文件中,直接看下代码
package client.test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import client.message.SimpleProtoBuf;
public class TestSimpleProtoBuf {
public static void main(String[] args) throws IOException{
//按照自定义的proto创建一个FigureInfo
//序列化过程
//由编译器胜自动生成的message类是不可变的,一旦一个message对象创建后,就像Java中的String类一样是不可变的。创建一个message时,必须首先创建一个builder,设置必须的值后,再调用builder的build()方法
//builder的每个方法在消息修改后又返回builder,这个返回对象又可以调用其他方法。这种方式对于在同一行操作不同的方法提供了便利。
SimpleProtoBuf.FigureInfo.Builder figureInfoBuilder = SimpleProtoBuf.FigureInfo.newBuilder();
figureInfoBuilder.setID(123);
figureInfoBuilder.setName("waylyn");
figureInfoBuilder.setAddress("上海市");
figureInfoBuilder.addFigureInfos("figure1");
figureInfoBuilder.addFigureInfos("figure2");
SimpleProtoBuf.FigureInfo figureInfoSend = figureInfoBuilder.build();
//-------------------发送方--------------------------------
ByteArrayOutputStream output = new ByteArrayOutputStream();
figureInfoSend.writeTo(output);
//-------------------接收方----------------------------------
byte[] byteArray = output.toByteArray();
ByteArrayInputStream input = new ByteArrayInputStream(byteArray);
//解析及反序列化
//parseFrom()从一个特定的字节数组解析成消息(就是将传输的字节流转换成程序需要的数据类型)
SimpleProtoBuf.FigureInfo figureInfoAccept = SimpleProtoBuf.FigureInfo.parseFrom(input);
System.out.println(figureInfoAccept.getID());
System.out.println(figureInfoAccept.getName());
//System.out.println(figureInfoAccept.getFigureInfos(0));
List<String> figureInfos = figureInfoAccept.getFigureInfosList();
for (String figureInfo : figureInfos) {
System.out.println(figureInfo);
}
}
}
代码里面有很详细的解释,就不多说了。控制台打印的信息: 123
waylyn
figure1
figure2
下面我们将proto文件写复杂一点,加入嵌套的message和枚举enum,然后简单测试。首先看TestSimpleProtoBuf.proto文件
package client.message;
option java_package = "client.message";
option java_outer_classname = "SimpleProtoBuf";
message FigureInfo {
//ID不可为空
required int32 ID = 1;
//Name不可为空
required string Name = 2;
//可以不空也可以空
optional string address = 3;
//枚举
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
//嵌套的message
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2;
}
//可以不空也可以空,可以出现任意次(phone是嵌套的phoneNumber message类型)
repeated PhoneNumber phone = 4;
}
将上面的Java代码略作修改,坐下简单测试
package client.test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import client.message.SimpleProtoBuf;
import client.message.SimpleProtoBuf.FigureInfo.PhoneNumber;
public class TestSimpleProtoBuf {
public static void main(String[] args) throws IOException{
//按照自定义的proto创建一个FigureInfo
//序列化过程
//由编译器胜自动生成的message类是不可变的,一旦一个message对象创建后,就像Java中的String类一样是不可变的。创建一个message时,必须首先创建一个builder,设置必须的值后,再调用builder的build()方法
//builder的每个方法在消息修改后又返回builder,这个返回对象又可以调用其他方法。这种方式对于在同一行操作不同的方法提供了便利。
SimpleProtoBuf.FigureInfo.Builder figureInfoBuilder = SimpleProtoBuf.FigureInfo.newBuilder();
figureInfoBuilder.setID(123);
figureInfoBuilder.setName("waylyn");
figureInfoBuilder.setAddress("上海市");
//嵌套消息(创建FigureInfo类中PhoneBumber类对象)
SimpleProtoBuf.FigureInfo.PhoneNumber.Builder phoneNumberBuilder1 = SimpleProtoBuf.FigureInfo.PhoneNumber.newBuilder();
phoneNumberBuilder1.setNumber("152XXXX8035");
phoneNumberBuilder1.setType(SimpleProtoBuf.FigureInfo.PhoneType.MOBILE); //Type是FigureInfo message中嵌套的enum中列出的一种
SimpleProtoBuf.FigureInfo.PhoneNumber.Builder phoneNumberBuilder2 = SimpleProtoBuf.FigureInfo.PhoneNumber.newBuilder();
phoneNumberBuilder2.setNumber("02188888888");
phoneNumberBuilder2.setType(SimpleProtoBuf.FigureInfo.PhoneType.HOME);
//FigureInfo message中的phone是PhoneNumber对象的多次出现
figureInfoBuilder.addPhone(phoneNumberBuilder1);
figureInfoBuilder.addPhone(phoneNumberBuilder2);
SimpleProtoBuf.FigureInfo figureInfoSend = figureInfoBuilder.build();
//-------------------发送方--------------------------------
ByteArrayOutputStream output = new ByteArrayOutputStream();
figureInfoSend.writeTo(output);
//-------------------接收方----------------------------------
byte[] byteArray = output.toByteArray();
ByteArrayInputStream input = new ByteArrayInputStream(byteArray);
//解析及反序列化
//<span style="font-family: Arial, Helvetica, sans-serif;">parseFrom()从一个特定的字节数组解析成消息(就是将传输的字节流转换成程序需要的数据类型)</span>
SimpleProtoBuf.FigureInfo figureInfoAccept = SimpleProtoBuf.FigureInfo.parseFrom(input);
System.out.println(figureInfoAccept.getID());
System.out.println(figureInfoAccept.getName());
//System.out.println(figureInfoAccept.getFigureInfos(0));
List<PhoneNumber> phoneNumbers = figureInfoAccept.getPhoneList();
for (PhoneNumber phoneNumber : phoneNumbers) {
switch (phoneNumber.getType()) {
case MOBILE:
System.out.println("Mobile Phone: " + phoneNumber.getNumber());
break;
case HOME:
System.out.println("Home Phone: " + phoneNumber.getNumber());
break;
case WORK:
System.out.println("Work Phone: " + phoneNumber.getNumber());
break;
}
}
}
}
其实还是比较简单的,控制台信息:
123
waylyn
Mobile Phone: 152XXXX8035
Home Phone: 02188888888