前端后台以及游戏中使用Google Protocol Buffer详解
前端后台以及游戏中使用Google Protocol Buffer详解
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环境下编写
Mac与window命令唯一的区别就是需要将protoc
改成protoc.exe
前提是需要添加环境变量。
3、编写一个proto文件
文件保存为chat.proto
此proto
文件摘自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
进入到项目的跟目录执行以下编译命名,com/im/common/packets
为chat.proto
中的包名,chat.proto
文件放在com/im/common/packets
下。
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 google-protobuf
npm install -g browserify
5.2 使用browserify对文件进行编译打包
编写脚本保存为exports.js
var chatProto = require('./chat_pb');
module.exports = {
DataProto: chatProto
}
执行命令 browserify exports.js > chat.js
对chat_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 add
、pb-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);
}
6.5 Cocos Creator中使用protobuf
Cocos Creator中使用protobuf与前端中使用protobuf操作步骤基本一样,只是在Cocos Creator中需要将JS导入为插件
将5.2中编译生成的JS导入到工程的脚本文件夹中,打开Cocos Creator就会提示您是否要将脚本设置为插件。
项目中能使用
与6.1前端中使用方式一样。
start: function () {
cc.log("start");
let 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");
let bytes = chatReqBody.serializeBinary();
cc.log("序列化为字节:" + bytes);
let data = proto.com.im.common.packets.ChatReqBody.deserializeBinary(bytes);
cc.log("反序列化为对象:" + data);
cc.log("从对象中获取指定属性:" + data.getTonick());
cc.log("对象转化为JSON:" + JSON.stringify(data));
},
到这里如何使用protobuf就介绍完了,个人能力有限如有错误欢迎指正。你有更好的解决方案或者建议欢迎一起交流讨论,如有疑问欢迎留言。
前端后台以及游戏中使用Google Protocol Buffer详解的更多相关文章
- 在 go/golang语言中使用 google Protocol Buffer
怎么在go语言中实用google protocol Buffer呢? 现在的潮流趋势就是一键搞定,跟ubuntu安装软件一样 go get code.google.com/p/goprotobuf/{ ...
- Protocol Buffer详解
1.Protocol Buffer 概念 Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 ...
- Google Protocol Buffer 的使用和原理[转]
本文转自: http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构 ...
- Google Protocol Buffer 的使用和原理
Google Protocol Buffer 的使用和原理 Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式.它 ...
- 【Google Protocol Buffer】Google Protocol Buffer
http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ Google Protocol Buffer 的使用和原理 Protocol Buffers ...
- 转Google Protocol Buffer 的使用和原理
Google Protocol Buffer 的使用和原理 Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式.它 ...
- Google Protocol Buffer 的使用和原理(无论对存储还是数据交换,都是个挺有用的东西,有9张图做说明,十分清楚)
感觉Google Protocol Buffer无论对存储还是数据交换,都是个挺有用的东西,这里记录下,以后应该用得着.下文转自: http://www.ibm.com/developerworks/ ...
- 【神经网络与深度学习】Google Protocol Buffer介绍
简介 什么是 Google Protocol Buffer? 假如您在网上搜索,应该会得到类似这样的文字介绍: Google Protocol Buffer( 简称 Protobuf) 是 Googl ...
- (转)Google Protocol Buffer 的使用和原理
转自:https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html 简介 什么是 Google Protocol Buffer? ...
随机推荐
- UOJ#310 【UNR #2】黎明前的巧克力 FWT 多项式
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ310.html 题目传送门 - UOJ#310 题意 给定 $n$ 个数 ,请你选出两个不相交的集合(两个 ...
- Spring(三)实例化Bean以及注入对象
使用xml实例化bean 在xml中实例化bean的三种方式 <bean id="springService" class="com.zhiyou100.crm.t ...
- es 模块的基础知识,深度了解
// 一模块的基础知识 /** * export :用于模块输出的出口 * import :文件引入的入口 */ // 1,第一种方式使用export方式输出 var a = 'a'; var b = ...
- 【python】TCP/IP编程
No1: [TCP] 客户端 import socket s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(('www.sina ...
- Masquerade strikes back Gym - 101911D (数学)
Quite often the jury of Saratov SU use the problem "Masquerade" in different practice sess ...
- js日期处理函数 -- 判断闰年,获取当月的总天数、添加月份
1. 判断是否是闰年 function isLeapYear(eDate) { var year = eDate.getFullYear(); return (((0 == year % 4) &am ...
- mybatis查询语句的背后之封装数据
转载请注明出处... 一.前言 继上一篇mybatis查询语句的背后,这一篇主要围绕着mybatis查询的后期操作,即跟数据库交互的时候.由于本人也是一边学习源码一边记录,内容难免有错误或不足之处,还 ...
- XamarinSQLite教程添加测试数据
XamarinSQLite教程添加测试数据 此时创建的Students表中是没有任何数据,也就是一个空表.为了方便测试App,开发者需要为表添加一些数据.操作步骤如下. (1)右击创建的Student ...
- DRF 权限 频率
DRF的权限 权限是什么 大家之前都应该听过权限~那么我们权限到底是做什么用的呢~~ 大家都有博客~或者去一些论坛~一定知道管理员这个角色~ 比如我们申请博客的时候~一定要向管理员申请~也就是说管理员 ...
- JavaScrip两个函数的设置为回调
1.javascript异步编程之回调函数 function fn2(data){ alert(data) } function fn1(callback){ var data = 12+1; cal ...