WebSocket引入protobuf協議

轉載自:https://blog.csdn.net/iningwei/article/details/100107254

前言
nodejs遊戲服務器開發系列文章以nodejs+Typescript+CocosCreator+WebSocket爲例,搭建服務器和客戶端。

protobuf
暫時無純ts框架的pb庫,一般會使用protobufjs,git地址:https://github.com/protobufjs/protobuf.js
同時也可以參考這個文檔:https://www.npmjs.com/package/protobufjs

protobufjs有很多不同的版本(minimal,light,full),用戶根據自己的項目需要選擇合適的版本。各個版本的說明,可以參考git上readme.

建議不要使用git中releases的庫,裏面更新的慢。使用npm install protobufjs會把庫下載到本地。可以看到CHANGELOG.md文檔中寫的是6.8.8.而在releases中最新的才6.8.6。

另外據說6以上版本protobufjs已經集成了long,bytebuffer庫,不需要額外集成了。

本系列文章使用的是完整的protobufjs庫(dist/protobuf.js)。

proto文件
搞個測試文件,比如命名爲test.proto,內容爲:

message user{
    required int32 userId=1;
    required string userName=2;
}

靜態文件
推薦使用靜態文件的方式,即預先把proto文件翻譯爲js文件。

需要全局安裝pbjsnpm install protobufjs -g。可以使用pbjs命令用於把proto文件翻譯爲js靜態文件。然後還可以使用pbts命令獲得對應的聲明文件*.d.ts

pbjs -t static-module -w commonjs -o proto.js *.proto
把目錄下所有的proto文件一起生成靜態文件到proto.js中

pbts -o proto.d.ts proto.js
爲proto.js文件生成生命文件:proto.d.ts。

最終會生成proto.js和proto.d.ts文件

靜態方式缺點,會額外生成靜態文件,導致包體變大,特別是微信小遊戲這種對包體敏感的平臺。

服務端集成protobufjs
src目錄下新建pb文件夾,把上述生成的靜態文件proto.js,proto.d.ts和protobuf.js都放到pb文件夾中。

修改proto.js的var $protobuf = require("protobufjs/minimal");爲var $protobuf = require("./protobuf");
修改proto.d.ts的第一句import * as $protobuf from "protobufjs";爲import * as $protobuf from "./protobuf";

複製以下內容到 index.ts

import * as WebSocket from "ws"
import proto = require("./pb/proto");

const server = new WebSocket.Server({ port: 8083 });
server.on("listening", () => {
    console.log("服務器啓動完畢!開始偵聽");
});

server.on("connection", function connection(ws) {
    ws.on("message", function incoming(message) {
        console.log("received:%s", message);

        let u = proto.user.decode(<Uint8Array>message);
        console.log("decode, userid:" + u.userId + ", username:" + u.userName);
    });
    ws.send("hhhello");
});

服務端報錯
ctrl+shift+b build的時候順利。
Debug開啓服務器的時候index.ts 報錯:Error: Cannot find module ‘./pb/proto’
一開始一直在查模塊方向的錯誤。
後面才恍然大悟:我服務器是用ts開發,最終生成的代碼是在dist目錄中。
編譯的時候只是把ts代碼編譯到了dist目錄,但是proto.js文件和protobuf.js文件我還放在src目錄下的pb文件夾呢。

於是在dist目錄中創建一個pb文件夾,同時把proto.js,protobuf.js拷貝過去,再次運行一切順利。

客戶端集成protobufjs
assets目錄下新建文件夾:pb
把上述提到的protobuf.js放入pb目錄下。設置導入爲插件。同時還要勾選允許編輯器加載。
同時把proto.js和proto.d.ts放到pb目錄下

CososCreator在構建時候會將我們編輯器裏所有的js腳本都打包到一個project.js的文件中,原生(native)的話就是project.jsc。
如果我們的protobuf.js打包進去就會報錯了,所以這裏需要導入爲插件,這樣做就不會被打包進project.js文件中。

同時由於客戶端中已經設置了插件模式。故proto.js中這句var $protobuf = require("protobufjs/minimal");需要改成:var $protobuf=protobuf;

還要修改proto.d.ts的第一句import * as $protobuf from "protobufjs";爲import * as $protobuf from "./protobuf";

客戶端代碼如下:

    private ws: WebSocket;
    start() {
        console.log("go!");
        this.ws = new WebSocket("ws://192.168.2.31:8083");
        this.ws.binaryType="arraybuffer";        
        this.ws.onopen = this.onOpen.bind(this);
        this.ws.onmessage = function (event) {
            console.log("client rcv:" + event.data);
        }
        this.ws.onclose = function (event) {
        }.bind(this);
        this.ws.onerror = function (event) {
        }
    }


    private onOpen(event: MessageEvent) {
        console.log("連接建立啦");
        
        let u: user = new user()
        u.userId = 1;
        u.userName = "toms";
        
        let encoded = proto.user.encode(u).finish();
        this.sendData(encoded);

        let xxx = proto.user.decode(encoded);
        console.log("嘿嘿:" + xxx.userName);
    }
    private sendData(data) {
        this.ws.send(data);
    }

注意客戶端設置了binaryType爲arraybuffer(筆者註釋了這行代碼,也沒有發現有啥問題)

需要注意:客戶端decode後還要調用finish(),不然服務端那邊解析不出來。

參考文檔:
TS項目中使用Protobuf的解決方案

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章