zoukankan      html  css  js  c++  java
  • 前端后台以及游戏中使用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:protobufprotoc-3.5.1-win32in

    本文在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

    进入到项目的跟目录执行以下编译命名,com/im/common/packetschat.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 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);
        }
    
    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就介绍完了,个人能力有限如有错误欢迎指正。你有更好的解决方案或者建议欢迎一起交流讨论,如有疑问欢迎留言。

  • 相关阅读:
    apache安装
    docker搭建redis主从
    docker安装
    sklearn工具-绪论
    数学基础-矩阵和线性代数
    数学基础-概率论与贝叶斯先验
    数学基础-数学分析
    数据科学包——Matplotlib
    数据科学包——pandas
    数据科学包——numpy
  • 原文地址:https://www.cnblogs.com/zyw-205520/p/9549464.html
Copyright © 2011-2022 走看看