當前位置:
首頁 > 知識 > 每日一博丨前端後台以及遊戲中使用google-protobuf詳解

每日一博丨前端後台以及遊戲中使用google-protobuf詳解

每日一博丨前端後台以及遊戲中使用google-protobuf詳解

歡迎下載開源中國APP獲取更多優質文章

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.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

進入到項目的根目錄執行以下編譯命令,proto文件存放在com/im/common/packets包下,com/im/common/packets為proto文件中的包名。

protoc --java_out=./ com/im/common/packets/chat.proto

4.2 編譯為JS

protoc --js_out=import_stylex=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.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);
}

到這裡如何使用protobuf就介紹完了,個人能力有限如有錯誤歡迎指正。你有更好的解決方案或者建議歡迎一起交流討論,如有疑問歡迎留言。

博客作者:Javen

每日一博欄目,每日為你推薦優秀博主的優質技術文章。同時歡迎用戶投稿,文章一旦被官方賬號收錄,我們會在網站首頁等位置進行推薦哦。點擊「了解更多」,每日獲取優質推送。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 OSC開源社區 的精彩文章:

每日一博丨精講Redis內存模型
Linus 又開懟:有時候標準就是一坨 shi!

TAG:OSC開源社區 |