前端后台以及游戏中使用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

  1. #protobuf
  2. export PROTOBUF_HOME=/Users/Javen/Documents/dev/java/protobuf/protoc-3.5.1-osx-x86_64
  3. 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 让天下没有难开发的网络编程

  1. syntax = "proto3";
  2. package com.im.common.packets;
  3. option java_package = "com.im.common.packets"; //设置java对应的package
  4. option java_multiple_files = true; //建议设置为true,这样会每个对象放在一个文件中,否则所有对象都在一个java文件中
  5. /**
  6. * 聊天类型
  7. */
  8. enum ChatType {
  9. CHAT_TYPE_UNKNOW = 0;//未知
  10. CHAT_TYPE_PUBLIC = 1;//公聊
  11. CHAT_TYPE_PRIVATE = 2;//私聊
  12. }
  13. /**
  14. * 聊天请求
  15. */
  16. message ChatReqBody {
  17. int64 time = 1;//消息发送时间
  18. ChatType type = 2; //聊天类型
  19. string text = 3; //聊天内容
  20. string group = 4; //目标组id
  21. int32 toId = 5; //目标用户id,
  22. string toNick = 6; //目标用户nick
  23. }
  24. /**
  25. * 聊天响应
  26. */
  27. message ChatRespBody {
  28. int64 time = 1;//消息发送时间
  29. ChatType type = 2; //聊天类型
  30. string text = 3; //聊天内容
  31. int32 fromId = 4; //发送聊天消息的用户id
  32. string fromNick = 5; //发送聊天消息的用户nick
  33. int32 toId = 6; //目标用户id
  34. string toNick = 7; //目标用户nick
  35. string group = 8; //目标组id
  36. }

4、编译器对其进行编译

4.1 编译为Java

进入到项目的跟目录执行以下编译命名,com/im/common/packetschat.proto中的包名,chat.proto文件放在com/im/common/packets下。

  1. protoc --java_out=./ com/im/common/packets/chat.proto
4.2 编译为JS
  1. 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文件的同级目录下安装引用库

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

编写脚本保存为exports.js

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

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

6、protobuf使用示例

6.1 前端(JavaScript)中使用protobuf
  1. <script src="./chat.js"></script>
  2. <script type="text/javascript">
  3. var chatReqBody = new proto.com.im.common.packets.ChatReqBody();
  4. chatReqBody.setTime(new Date().getTime());
  5. chatReqBody.setText("测试");
  6. chatReqBody.setType(1);
  7. chatReqBody.setGroup("Javen");
  8. chatReqBody.setToid(666);
  9. chatReqBody.setTonick("Javen205");
  10. var bytes = chatReqBody.serializeBinary();
  11. console.log("序列化为字节:"+bytes);
  12. var data = proto.com.im.common.packets.ChatReqBody.deserializeBinary(bytes);
  13. console.log("反序列化为对象:"+data);
  14. console.log("从对象中获取指定属性:"+data.getTonick());
  15. console.log("对象转化为JSON:"+JSON.stringify(data));
  16. </script>
6.2 Java中使用protobuf

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

  1. <dependency>
  2. <groupId>com.google.protobuf</groupId>
  3. <artifactId>protobuf-java</artifactId>
  4. <version>3.5.1</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.googlecode.protobuf-java-format</groupId>
  8. <artifactId>protobuf-java-format</artifactId>
  9. <version>1.4</version>
  10. </dependency>
  1. public static void test() {
  2. try {
  3. JsonFormat jsonFormat = new JsonFormat();
  4. ChatRespBody.Builder builder = ChatRespBody.newBuilder();
  5. builder.setType(ChatType.CHAT_TYPE_PUBLIC);
  6. builder.setText("Javen 测试");
  7. builder.setFromId(1);
  8. builder.setFromNick("Javen");
  9. builder.setToId(110);
  10. builder.setToNick("Javen.zhou");
  11. builder.setGroup("Javen");
  12. builder.setTime(SystemTimer.currentTimeMillis());
  13. ChatRespBody chatRespBody = builder.build();
  14. //从protobuf转json
  15. String asJson = jsonFormat.printToString(chatRespBody);
  16. System.out.println("Object to json "+asJson);
  17. byte[] bodybyte = chatRespBody.toByteArray();
  18. //解码是从byte[]转换为java对象
  19. ChatRespBody parseChatRespBody = ChatRespBody.parseFrom(bodybyte);
  20. asJson = jsonFormat.printToString(parseChatRespBody);
  21. System.out.println("bodybyte to json "+asJson);
  22. //从json转protobuf
  23. ChatRespBody.Builder _builder = ChatRespBody.newBuilder();
  24. jsonFormat.merge(new ByteArrayInputStream(asJson.getBytes()), _builder);
  25. ChatRespBody _chatRespBody = _builder.build();
  26. asJson = jsonFormat.printToString(_chatRespBody);
  27. System.out.println("json to protobuf "+asJson);
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. }
  31. }
6.3 QQ玩一玩中使用protobuf

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

  1. // var global = Function('return this')();
  2. var global = (function(){
  3. return this;
  4. })()

  1. BK.Script.loadlib('GameRes://qqPlayCore.js');
  2. BK.Script.loadlib('GameRes://tio/chat.js');
  3. function test() {
  4. var ws = new BK.WebSocket("ws://127.0.0.1:9326?group=test&name=Javen");
  5. ws.onOpen = function(ws) {
  6. BK.Script.log(1, 0, "onOpen.js");
  7. BK.Script.log(1, 0, "1.readyState = " + ws.getReadyState());
  8. var time = 0;
  9. BK.Director.ticker.add(function(ts, duration) {
  10. time = time + 1;
  11. if (time % 100 == 0) {
  12. // ws.send("phone test" + time);
  13. var chatReqBody = new proto.com.im.common.packets.ChatReqBody();
  14. chatReqBody.setTime(new Date().getTime());
  15. chatReqBody.setText("phone test" + time);
  16. chatReqBody.setType(1);
  17. chatReqBody.setGroup("test");
  18. var bytes = chatReqBody.serializeBinary();
  19. ws.send(bytes);
  20. }
  21. });
  22. };
  23. ws.onClose = function(ws) {
  24. BK.Script.log(1, 0, "onClose.js");
  25. BK.Script.log(1, 0, "1.readyState = " + ws.getReadyState());
  26. };
  27. ws.onError = function(ws) {
  28. BK.Script.log(1, 0, "onError.js");
  29. BK.Script.log(1, 0, "1.readyState = " + ws.getReadyState());
  30. BK.Script.log("onError.js.js getErrorCode:" + ws.getErrorCode());
  31. BK.Script.log("onError.js getErrorString:" + ws.getErrorString());
  32. };
  33. ws.onMessage = function(ws, event) {
  34. if (!event.isBinary) {
  35. var str = event.data.readAsString();
  36. BK.Script.log(1, 0, "text = " + str);
  37. } else {
  38. var buf = event.data;
  39. //将游标pointer重置为0
  40. buf.rewind();
  41. var ab = new ArrayBuffer(buf.length);
  42. var dv = new DataView(ab);
  43. while (!buf.eof) {
  44. dv.setUint8(buf.pointer, buf.readUint8Buffer());
  45. }
  46. var chatRespBody = proto.com.im.common.packets.ChatRespBody.deserializeBinary(ab);
  47. var msg = chatRespBody.getFromnick() + " 说: " + chatRespBody.getText();
  48. BK.Script.log(1, 0, "text = " + msg);
  49. }
  50. };
  51. ws.onSendComplete = function(ws) {
  52. BK.Script.log(1, 0, "onSendComplete.js");
  53. };
  54. ws.connect();
  55. }
  56. test();
6.4 Eget中使用protobuf
插件下载

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

  1. npm install protobufjs -g
  2. 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节点添加

  1. {
  2. "name": "protobuf-library",
  3. "path": "protobuf/library"
  4. },
  5. {
  6. "name": "protobuf-bundles",
  7. "path": "protobuf/bundles"
  8. }

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

项目中能使用

处理发送消息

  1. private sendReq(text:string,group:string){
  2. var chatReqBody = new com.im.common.packets.ChatReqBody();
  3. chatReqBody.time = new Date().getTime();
  4. chatReqBody.text = text;
  5. chatReqBody.type = com.im.common.packets.ChatType.CHAT_TYPE_PUBLIC;
  6. chatReqBody.group = group;
  7. let data = com.im.common.packets.ChatReqBody.encode(chatReqBody).finish();
  8. this.sendBytesData(data);
  9. }
  10. private sendBytesData(data:Uint8Array){
  11. this.socket.writeBytes(new egret.ByteArray(data));
  12. }

处理接收消息

  1. private onReceiveMessage(e:egret.Event):void {
  2. //创建 ByteArray 对象
  3. var byte:egret.ByteArray = new egret.ByteArray();
  4. //读取数据
  5. this.socket.readBytes(byte);
  6. let buffer = new Uint8Array(byte.buffer);
  7. let chatRespBody = com.im.common.packets.ChatRespBody.decode(buffer);
  8. // this.trace("收到数据:"+JSON.stringify(chatRespBody));
  9. this.trace(chatRespBody.fromNick+" 说: "+chatRespBody.text);
  10. }
6.5 Cocos Creator中使用protobuf

Cocos Creator中使用protobuf与前端中使用protobuf操作步骤基本一样,只是在Cocos Creator中需要将JS导入为插件

5.2中编译生成的JS导入到工程的脚本文件夹中,打开Cocos Creator就会提示您是否要将脚本设置为插件。

项目中能使用

与6.1前端中使用方式一样。

  1. start: function () {
  2. cc.log("start");
  3. let chatReqBody = new proto.com.im.common.packets.ChatReqBody();
  4. chatReqBody.setTime(new Date().getTime());
  5. chatReqBody.setText("测试");
  6. chatReqBody.setType(1);
  7. chatReqBody.setGroup("Javen");
  8. chatReqBody.setToid(666);
  9. chatReqBody.setTonick("Javen205");
  10. let bytes = chatReqBody.serializeBinary();
  11. cc.log("序列化为字节:" + bytes);
  12. let data = proto.com.im.common.packets.ChatReqBody.deserializeBinary(bytes);
  13. cc.log("反序列化为对象:" + data);
  14. cc.log("从对象中获取指定属性:" + data.getTonick());
  15. cc.log("对象转化为JSON:" + JSON.stringify(data));
  16. },

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

前端后台以及游戏中使用Google Protocol Buffer详解的更多相关文章

  1. 在 go/golang语言中使用 google Protocol Buffer

    怎么在go语言中实用google protocol Buffer呢? 现在的潮流趋势就是一键搞定,跟ubuntu安装软件一样 go get code.google.com/p/goprotobuf/{ ...

  2. Protocol Buffer详解

    1.Protocol Buffer 概念 Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 ...

  3. Google Protocol Buffer 的使用和原理[转]

    本文转自: http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构 ...

  4. Google Protocol Buffer 的使用和原理

    Google Protocol Buffer 的使用和原理 Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式.它 ...

  5. 【Google Protocol Buffer】Google Protocol Buffer

    http://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ Google Protocol Buffer 的使用和原理 Protocol Buffers ...

  6. 转Google Protocol Buffer 的使用和原理

    Google Protocol Buffer 的使用和原理 Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,很适合做数据存储或 RPC 数据交换格式.它 ...

  7. Google Protocol Buffer 的使用和原理(无论对存储还是数据交换,都是个挺有用的东西,有9张图做说明,十分清楚)

    感觉Google Protocol Buffer无论对存储还是数据交换,都是个挺有用的东西,这里记录下,以后应该用得着.下文转自: http://www.ibm.com/developerworks/ ...

  8. 【神经网络与深度学习】Google Protocol Buffer介绍

    简介 什么是 Google Protocol Buffer? 假如您在网上搜索,应该会得到类似这样的文字介绍: Google Protocol Buffer( 简称 Protobuf) 是 Googl ...

  9. (转)Google Protocol Buffer 的使用和原理

    转自:https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html   简介 什么是 Google Protocol Buffer? ...

随机推荐

  1. php通过CURL模拟get提交请求

    方式一: $host = "http://jisunews.market.alicloudapi.com"; $path = "/news/get"; $met ...

  2. fillder--信息面板显示请求耗时列

    class Handlers--------->Ctrl+R,找到该方法,加上以下方法即可 { // 显示每行请求的发起时间:时分秒毫秒 public static BindUIColumn(& ...

  3. psp表格记录-

    PSP2.1 Personal Software Process Stages Time Planning 计划 · Estimate · 估计这个任务需要多少时间 12 Development 开发 ...

  4. P1030 求先序排列 P1305 新二叉树

    题目描述 给出一棵二叉树的中序与后序排列.求出它的先序排列.(约定树结点用不同的大写字母表示,长度\le 8≤8). 输入输出格式 输入格式: 22行,均为大写字母组成的字符串,表示一棵二叉树的中序与 ...

  5. MATLAB视频读取转换为图片

    转换mp4到jpg格式的图片: % convert .mp4 to jpg picture t='C:\Documents and Settings\luokh\桌面\Matlab编程\Matlab编 ...

  6. 关闭selinux服务

    vim /etc/selinux/config 将SELINUX=disabled 执行setenforce 0

  7. Activity的启动

    --摘自<android插件化开发指南> 1.AMS管理着四大组件 2.为什么Hook不能在AMS那边?因为AMS属于android系统,android系统可以被Hook,那就是病毒了.四 ...

  8. Editor HDU - 4699 (栈)

    Problem Description   Sample Input 8 I 2 I -1 I 1 Q 3 L D R Q 2   Sample Output 2 3 Hint The followi ...

  9. Django之win7下安装与命令行工具

    Django之win7下安装与命令行工具 下载安装 pip3 install django 注意:自动添加环境变量 测试是否安装成功 1.输入python 2.输入import django 3.输入 ...

  10. 高效使用hibernate-validator校验框架

    一.前言 高效.合理的使用hibernate-validator校验框架可以提高程序的可读性,以及减少不必要的代码逻辑.接下来会介绍一下常用一些使用方式. 二.常用注解说明 限制 说明 @Null 限 ...