protocol buffer从0学起(一)

        其实前面的前面的一篇文章写过关于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.csdn.net/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

猜你喜欢

转载自blog.csdn.net/u014489596/article/details/39371761