文档章节

前端后台以及游戏中使用google-protobuf详解

Javen-IJPay
 Javen-IJPay
发布于 06/10 16:26
字数 1556
阅读 3500
收藏 38

前端后台以及游戏中使用google-protobuf详解

[TOC]

0、什么是protoBuf

protoBuf是一种灵活高效的独立于语言平台的结构化数据表示方法,与XML相比,protoBuf更小更快更简单。你可以用定义自己protoBuf的数据结构,用ProtoBuf编译器生成特定语言的源代码,如C++,Java,Python等,目前protoBuf对主流的编程语言都提供了支持,非常方便的进行序列化和反序列化。

特点:

  • 平台无关、语言无关。
  • 二进制、数据自描述。
  • 提供了完整详细的操作API。
  • 高性能 比xml要快20-100倍
  • 尺寸小 比xml要小3-10倍 高可扩展性
  • 数据自描述、前后兼容

1、下载protobuf的编译器

目前最新版本为Protocol Buffers v3.5.1

2、配置环境变量

解压 protoc-3.5.1-osx-x86_64.zip

Mac 配置环境变量 vi ~/.bash_profile 使其配置生效source ~/.bash_profile

#protobuf
export PROTOBUF_HOME=/Users/Javen/Documents/dev/java/protobuf/protoc-3.5.1-osx-x86_64
export PATH=$PATH:$PROTOBUF_HOME/bin

Window 将bin添加到path 即可 例如:D:\protobuf\protoc-3.5.1-win32\bin

本文在Mac环境下编写 Macwindow命令唯一的区别就是需要将protoc改成protoc.exe 前提是需要添加环境变量。

3、编写一个proto文件

文件保存为chat.protoproto文件摘自t-io 让天下没有难开发的网络编程

syntax = "proto3";
package com.im.common.packets;

option java_package = "com.im.common.packets";  //设置java对应的package
option java_multiple_files = true; //建议设置为true,这样会每个对象放在一个文件中,否则所有对象都在一个java文件中

/**
 * 聊天类型
 */
enum ChatType {
	CHAT_TYPE_UNKNOW = 0;//未知
	CHAT_TYPE_PUBLIC = 1;//公聊
	CHAT_TYPE_PRIVATE = 2;//私聊
}
/**
 * 聊天请求
 */
message ChatReqBody {
	int64 time = 1;//消息发送时间
	ChatType type = 2; //聊天类型
	string text = 3; //聊天内容
	string group = 4; //目标组id
	int32 toId = 5; //目标用户id,
	string toNick = 6; //目标用户nick
}

/**
 * 聊天响应
 */
message ChatRespBody {
	int64 time = 1;//消息发送时间
	ChatType type = 2; //聊天类型
	string text = 3; //聊天内容
	int32 fromId = 4; //发送聊天消息的用户id
	string fromNick = 5; //发送聊天消息的用户nick
	int32 toId = 6; //目标用户id
	string toNick = 7; //目标用户nick
	string group = 8; //目标组id
}

4、编译器对其进行编译

4.1 编译为Java

进入到项目的根目录执行以下编译命令,proto文件存放在com/im/common/packets包下,com/im/common/packetsproto文件中的包名。

protoc  --java_out=./  com/im/common/packets/chat.proto
4.2 编译为JS
protoc --js_out=import_style=commonjs,binary:. chat.proto

执行后会在当前文件夹中生成chat_pb.js 文件,这里面就是protobuf的API和一些函数。如果是Node.js 就可以直接使用了,如果想在浏览器(前端)中使用protobuf还需要做一些处理。

5、前端使用protobuf处理步骤

5.1 npm安装需要的库

chat_pb.js文件的同级目录下安装引用库

npm install -g require
npm install -g browserify
npm install google-protobuf
5.2 使用browserify对文件进行编译打包

编写脚本保存为exports.js

var chatProto = require('./chat_pb');  
module.exports = {  
DataProto: chatProto  
}

执行命令 browserify exports.js > chat.jschat_pb.js文件进行编译打包生成chat.js后就可以愉快的使用了。

6、protobuf使用示例

6.1 前端(JavaScript)中使用protobuf
<script src="./chat.js"></script>
<script type="text/javascript">
    var chatReqBody = new proto.com.im.common.packets.ChatReqBody();
    chatReqBody.setTime(new Date().getTime());
    chatReqBody.setText("测试");
    chatReqBody.setType(1);
    chatReqBody.setGroup("Javen");
    chatReqBody.setToid(666);
    chatReqBody.setTonick("Javen205");

    var bytes = chatReqBody.serializeBinary();  
    console.log("序列化为字节:"+bytes);
    var data = proto.com.im.common.packets.ChatReqBody.deserializeBinary(bytes); 
    console.log("反序列化为对象:"+data);  
    console.log("从对象中获取指定属性:"+data.getTonick());
    console.log("对象转化为JSON:"+JSON.stringify(data));  

</script>
6.2 Java中使用protobuf

java中要用protobuf,protobuf与json相互转换,首先需要引入相关的jar,maven的pom坐标如下

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>com.googlecode.protobuf-java-format</groupId>
    <artifactId>protobuf-java-format</artifactId>
    <version>1.4</version>
</dependency>
public static void test() {
		try {
			JsonFormat jsonFormat = new JsonFormat();
			ChatRespBody.Builder builder = ChatRespBody.newBuilder();
			builder.setType(ChatType.CHAT_TYPE_PUBLIC);
			builder.setText("Javen 测试");
			builder.setFromId(1);
			builder.setFromNick("Javen");
			builder.setToId(110);
			builder.setToNick("Javen.zhou");
			builder.setGroup("Javen");
			builder.setTime(SystemTimer.currentTimeMillis());
			ChatRespBody chatRespBody = builder.build();
			//从protobuf转json
			String asJson = jsonFormat.printToString(chatRespBody);
			System.out.println("Object to json "+asJson);
			
			byte[] bodybyte = chatRespBody.toByteArray();
			//解码是从byte[]转换为java对象
			ChatRespBody parseChatRespBody = ChatRespBody.parseFrom(bodybyte);
			asJson = jsonFormat.printToString(parseChatRespBody);
			System.out.println("bodybyte to json "+asJson);
			
			//从json转protobuf
			ChatRespBody.Builder _builder = ChatRespBody.newBuilder();
			jsonFormat.merge(new ByteArrayInputStream(asJson.getBytes()), _builder);
			ChatRespBody _chatRespBody = _builder.build();
			asJson = jsonFormat.printToString(_chatRespBody);
			System.out.println("json to protobuf "+asJson);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
6.3 QQ玩一玩中使用protobuf

chat.js中的var global = Function('return this')();修改为

// var global = Function('return this')();

var global = (function(){
  return this;
})()


BK.Script.loadlib('GameRes://qqPlayCore.js');
BK.Script.loadlib('GameRes://tio/chat.js');

function test() {
	var ws = new BK.WebSocket("ws://127.0.0.1:9326?group=test&name=Javen");
	ws.onOpen = function(ws) {
		BK.Script.log(1, 0, "onOpen.js");
		BK.Script.log(1, 0, "1.readyState = " + ws.getReadyState());

		var time = 0;
		BK.Director.ticker.add(function(ts, duration) {
			time = time + 1;
			if (time % 100 == 0) {
				// ws.send("phone test" + time);
				var chatReqBody = new proto.com.im.common.packets.ChatReqBody();
				chatReqBody.setTime(new Date().getTime());
				chatReqBody.setText("phone test" + time);
				chatReqBody.setType(1);
				chatReqBody.setGroup("test");
				var bytes = chatReqBody.serializeBinary();
				ws.send(bytes);
			}
		});
	};
	ws.onClose = function(ws) {
		BK.Script.log(1, 0, "onClose.js");
		BK.Script.log(1, 0, "1.readyState = " + ws.getReadyState());
	};
	ws.onError = function(ws) {
		BK.Script.log(1, 0, "onError.js");
		BK.Script.log(1, 0, "1.readyState = " + ws.getReadyState());
		BK.Script.log("onError.js.js getErrorCode:" + ws.getErrorCode());
		BK.Script.log("onError.js getErrorString:" + ws.getErrorString());
	};
	ws.onMessage = function(ws, event) {
		if (!event.isBinary) {
			var str = event.data.readAsString();
			BK.Script.log(1, 0, "text = " + str);
		} else {
			var buf = event.data;
			//将游标pointer重置为0
			buf.rewind();
			var ab = new ArrayBuffer(buf.length);
			var dv = new DataView(ab);
			while (!buf.eof) {
				dv.setUint8(buf.pointer, buf.readUint8Buffer());
			}
			var chatRespBody = proto.com.im.common.packets.ChatRespBody.deserializeBinary(ab);
			var msg = chatRespBody.getFromnick() + " 说: " + chatRespBody.getText();
			BK.Script.log(1, 0, "text = " + msg);
		}
	};
	ws.onSendComplete = function(ws) {
		BK.Script.log(1, 0, "onSendComplete.js");
	};
	ws.connect();
}

test();
6.4 Eget中使用protobuf
插件下载

egret有提供将proto文件生成JS以及TS的工具

npm install protobufjs -g
npm install @egret/protobuf -g
操作步骤

1、在白鹭项目的根目录中新建protobuf文件夹,再在protobuf文件夹中新建protofile文件夹

2、将proto文件放到protofile文件夹中

3、依次执行pb-egret addpb-egret generate

将会自动完成以下操作:

1、在tsconfig.json中的include节点中添加protobuf/**/*.d.ts

2、在egretProperties.json中的modules节点添加

{
"name": "protobuf-library",
"path": "protobuf/library"
},
{
"name": "protobuf-bundles",
"path": "protobuf/bundles"
}

3、在protobuf文件夹中自动生成bundles以及library文件夹里面包含了我们需要的js以及ts

项目中能使用

处理发送消息

 private sendReq(text:string,group:string){
        var chatReqBody = new com.im.common.packets.ChatReqBody();
        chatReqBody.time = new Date().getTime();
        chatReqBody.text = text;
        chatReqBody.type = com.im.common.packets.ChatType.CHAT_TYPE_PUBLIC;
        chatReqBody.group = group;
        let data = com.im.common.packets.ChatReqBody.encode(chatReqBody).finish();
        this.sendBytesData(data);
    }

    private sendBytesData(data:Uint8Array){
        this.socket.writeBytes(new egret.ByteArray(data));
    }

处理接收消息

 private onReceiveMessage(e:egret.Event):void {

        //创建 ByteArray 对象
        var byte:egret.ByteArray = new egret.ByteArray();
        //读取数据
        this.socket.readBytes(byte);
        let buffer = new Uint8Array(byte.buffer);
        let chatRespBody =  com.im.common.packets.ChatRespBody.decode(buffer);
        
        // this.trace("收到数据:"+JSON.stringify(chatRespBody));
        this.trace(chatRespBody.fromNick+" 说: "+chatRespBody.text);
    }

到这里如何使用protobuf就介绍完了,个人能力有限如有错误欢迎指正。你有更好的解决方案或者建议欢迎一起交流讨论,如有疑问欢迎留言。

© 著作权归作者所有

共有 人打赏支持
Javen-IJPay

Javen-IJPay

粉丝 42
博文 7
码字总数 6913
作品 3
深圳
程序员
加载中

评论(5)

久永
久永

引用来自“久永”的评论

怎么连个说明这个数据格式定义的文章资料链接都没有?

引用来自“Javen”的评论

https://developers.google.com/protocol-buffers/docs/proto3 大佬需要搭梯子
问的当然是不需要爬梯子的啊。简单搜了下看看,发现和我13年时候的思路有点类似,之前我还完全不知道有这个项目存在。当时就是面临json和xml又慢又臃肿,设想的一个思路。不过没有时间来实现,仅仅停留在了思路大纲上。
对于跨平台也思考过,但是因为跨平台有字节顺序问题,感觉工作量也不少(既要考虑兼容,又要性能不影响)。
整体思路来看,基本大纲的设想都实现了,虽然有的解决方式并不是特别好。(比如,我的思路原来设想的目标是能像json和xml一样自解释,但是谷歌舍弃了。并且自己搞了个模型编译,有点鸡肋。)
Javen-IJPay
Javen-IJPay

引用来自“久永”的评论

怎么连个说明这个数据格式定义的文章资料链接都没有?
https://developers.google.com/protocol-buffers/docs/proto3 大佬需要搭梯子
久永
久永
怎么连个说明这个数据格式定义的文章资料链接都没有?
Javen-IJPay
Javen-IJPay

引用来自“talent-tan”的评论

这个chat.proto,在老版本中才有,新版本已经把im相关的代码去掉了
嗯 这个已经不重要了,学习研究搞懂原理最重要。
talent-tan
talent-tan
这个chat.proto,在老版本中才有,新版本已经把im相关的代码去掉了
几种序列化协议(protobuf,xstream,jackjson,jdk,hessian)相关数据

别人的相关测试数据: http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking 测试纬度 序列化时间 反序列化时间 bytes大小 测试代码 准备protobuf文件 Message.proto文件代码...

SANSOM
2015/08/20
0
0
google的protobuf库

这篇文章将讲述如何使用google的protobuf库实现一个RPC service,就实现一个最简单的service吧:echo.文章对应的代码都可以在eventrpc中找到,写下这篇文章时的svn revision是138. 1) 定义协议首...

moki_oschina
06/06
0
0
Protobuffer | PHP安装Google protobuf及使用

PHP安装Google protobuf及使用 备注 项目采用redis集群(主从方式)存储数据;数据量月增50W,单个数据序列化情况下达到2k,继续压缩数据解决空间. 项目服务采用PHP(版本5.3)作为RPC服务版本. pro...

云迹
2017/02/05
0
0
开源点评:Protocol Buffers介绍

今天来介绍一下“Protocol Buffers”(以下简称protobuf)这个玩意儿。本来俺在构思“生产者/消费者模式 ”系列的下一个帖子:关于生产者和消费者之间的数据传输格式。由于里面扯到了protobu...

彭苏云
2014/09/26
0
0
基于Go语言的protobuf 安装 以及简单测试用例

先去官网下载protobuf的源码 https://github.com/google/protobuf/releases 可以先下载本地,然后上传到虚拟机中 我选择的是Source code(tar.gz) 安装依赖包(如果缺少包,可能会报错) yum i...

故新
07/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

20180920 rzsz传输文件、用户和用户组相关配置文件与管理

利用rz、sz实现Linux与Windows互传文件 [root@centos01 ~]# yum install -y lrzsz # 安装工具sz test.txt # 弹出对话框,传递到选择的路径下rz # 回车后,会从对话框中选择对应的文件传递...

野雪球
41分钟前
0
0
OSChina 周四乱弹 —— 毒蛇当辣条

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @ 达尔文:分享花澤香菜/前野智昭/小野大輔/井上喜久子的单曲《ミッション! 健?康?第?イチ》 《ミッション! 健?康?第?イチ》- 花澤香菜/前野智...

小小编辑
今天
5
2
java -jar运行内存设置

java -Xms64m #JVM启动时的初始堆大小 -Xmx128m #最大堆大小 -Xmn64m #年轻代的大小,其余的空间是老年代 -XX:MaxMetaspaceSize=128m # -XX:CompressedClassSpaceSize=6...

李玉长
今天
1
0
Spring | 手把手教你SSM最优雅的整合方式

HEY 本节主要内容为:基于Spring从0到1搭建一个web工程,适合初学者,Java初级开发者。欢迎与我交流。 MODULE 新建一个Maven工程。 不论你是什么工具,选这个就可以了,然后next,直至finis...

冯文议
今天
1
0
RxJS的另外四种实现方式(四)——性能最高的库(续)

接上一篇RxJS的另外四种实现方式(三)——性能最高的库 上一篇文章我展示了这个最高性能库的实现方法。下面我介绍一下这个性能提升的秘密。 首先,为了弄清楚Most库究竟为何如此快,我必须借...

一个灰
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部