在node中使用protobuf.js

自作多情 提交于 2019-11-29 05:42:13

本文基于node 6.9.x 使用的protobuf.js的版本 5.0.2

因为layabox 1.6.x引擎自带的protobuf.js的版本是5.0.1,考虑两边兼容,所以我在node服务器端使用5.0.2

我的目标用js同时实现手机端和服务器端,不用搞两套语言了,使用protobuf就不重造车轮了

首先下载安装
protobufjs  https://github.com/dcodeIO/protobuf.js/tree/5.0.2

用npm命令  npm install protobufjs@5

安装完成后,就可以使用了protobuf.js了



 

为了实战,我这里使用了多个proto文件

放到工程的proto目录下面

msghead.proto

---------------------------------------分割线开始-------------------------------------------------------

syntax = "proto3";

package lz;

 

enum MsgType {    //消息类型

Request = 0;  //请求类弄

Answer = 1;   //响应类型

Notice = 2;   //通知类型

NotMessage = 3; //不是消息

}

 

//消息头

message MsgHead

{

required int32 flag = 1 [default = 1978];   //消息标志,固定值为1978

required int32 id   = 2;   //MessageID

required MsgType type = 3 [default = 0]; //消息类型

}

---------------------------------------分割线结束-------------------------------------------------------

 

noticemsgid.proto

---------------------------------------分割线开始-------------------------------------------------------

syntax = "proto3";

package lz;

 

//这里的消息ID,只是通知消息

enum NoticeMsgID

{

GameOver = 1;

};

---------------------------------------分割线结束-------------------------------------------------------

msgid.proto

---------------------------------------分割线开始-------------------------------------------------------

syntax = "proto3";

package lz;

//这里的消息ID,对应的消息定义必须成对出现

enum MsgID

{

HelloWorld = 1;

};

---------------------------------------分割线结束-------------------------------------------------------

test.proto

---------------------------------------分割线开始-------------------------------------------------------

syntax = "proto3";

package lz.msg;

 

message ReqHelloWorld   //MsgID = HelloWorld

{

    required int32     id  = 1;  // ID

    required string    str = 2;  // str

    optional int32     opt = 3;  //optional field

}

 

message AnsHelloWorld

{

    required int32   Result = 1;      //处理结果

    optional string error_msg = 2;   //错误信息

}

 

message NoticeGameOver

{

    required int32 Result = 1;           //

};

---------------------------------------分割线结束-------------------------------------------------------

msg.proto

---------------------------------------分割线开始-------------------------------------------------------

//这里把所有的proto文件包含进来

package lz;

syntax = "proto3";

import "msghead.proto";

import "msgid.proto";

import "noticemsgid.proto";

import "test.proto";

---------------------------------------分割线结束-------------------------------------------------------

 

当然,也可以把这些放到一个文件中.
安装完成protobufjs后,在node_module/.bin/有pbjs.cmd 可以生成对应的proto的js代码
如:
pbjs D:\newgame\proto\msg.proto -t commonjs >d:\tmp\a.js


import fs from "fs";
import Protobuf from "protobuf";
import ByteBuffer from "ByteBuffer";
//消息管理器类,用于消息分发,这里只是demo,所以这个只有简单的功能
class MsgManager {
    constructor(){
        this._notice_map = new Map();
        this._request_map = new Map();
        this._answer_map = new Map();
        this._buffMsg = new ByteBuffer(4192);
    }    
    get notice_map() { return this._notice_map; }
    get request_map() { return this._request_map; }
    get answer_map() { return this._answer_map;}
         //消息编码  并放到this._buffMsg中
    encode_msg(msgDef, data) {
        let msgHead = this.MsgHead;                     //在关联的时候,将消息ID,类型与消息体关联了
        msgHead.id = msgDef._msgHead.id;
        msgHead.type = msgDef._msgHead.type;

        this._buffMsg.clear();            //清除缓冲
        this._buffMsg.writeInt32(0);  //预写入消息包的大小  4字节包体大小 + 2字节消息包头大小 + 消息包头数据 + 消息体数据

        let msgHeadSizeOffset = this._buffMsg.offset;  //这里记录消息包头大小偏移位置
        this._buffMsg.writeInt16(0);  //预写入消息包头大小
        msgHead.encode(this._buffMsg);  //消息头编码
        this._buffMsg.writeInt16(this._buffMsg.offset - msgHeadSizeOffset, msgHeadSizeOffset); //写往下正确的消息包头大小
        let msg = new msgDef(data);  
        msg.encode(this._buffMsg);  //消息体编码
        let msgSize = this._buffMsg.offset;
        this._buffMsg.writeInt32(this._buffMsg.offset, 0);  //写入正确的消息包大小
        console.log(this._buffMsg);
    }
     //解码处理
    decode_msg() {
        let bb = new ByteBuffer(this._buffMsg.offset);    
        this._buffMsg.copyTo(bb, 0, 0, this._buffMsg.offset);  //取出要解码的数据    在解码的过程中,数据实际的数据大于解码需要的数据,会抛出异常,所以在这里先把这个消息的数据复制出来,再解码,注:这里,暂时没有做拆包处理
        let msgSize = bb.readInt32();     //读取包大小
        let headSize = bb.readInt16();   //取消息头大小
        let head = this.lz.MsgHead.decode(bb, headSize-2);  //取得消息头数据
        console.log(head);
        let msg_map;
        switch(head.type)   //根据类型,取消息映射表
        {
            case this.MsgType.Request:
                msg_map = this.request_map;
                break;
            case this.MsgType.Answer:
                msg_map = this.answer_map;
                break;
            case this.MsgType.Notice:
                msg_map = this.notice_map;
                break;
        }
        let msg = msg_map.get(head.id).decode(bb);   //根据id,解码数据
        console.log(msg);
    }    
}



//这里是处理消息的关联
var msgMgr = new MsgManager();
function msg_process(messageMgr) {
    //读取proto文件,并生成相应的代码
    let protodata = fs.readFileSync("./proto/msg.proto").toString();  
    let lz = Protobuf.loadProto(protodata,null,"./proto/msg.proto").build("lz");   //生成package lz下面对像消息数据

    messageMgr.MsgType     = lz.MsgType;    //消息类型定义
    messageMgr.MsgID       = lz.MsgID;        //消息id定义
    messageMgr.NoticeMsgID = lz.NoticeMsgID; //通知消息定义
    messageMgr.Msg         = lz.msg;             //所有的消息定义
    messageMgr.MsgHead     = new lz.MsgHead();   //消息头对像,对于发送的时候,减少new的次数
    messageMgr.MsgHead.flag = 1978;
    messageMgr.lz          = lz;
    //关联:请求响应 消息
    for(let msgName in messageMgr.MsgID)
    {
        let msgReq = "Req" + msgName;  //请求消息名称
        let msgAns = "Ans" + msgName;  //响应消息名称
        let msgID = messageMgr.MsgID[msgName];  //对应的消息ID
        let req = lz.msg[msgReq];    //请求消息的消息定义对象
        let ans = lz.msg[msgAns];    //响应消息的消息定义对象
        req._msgHead = {id:msgID, type:messageMgr.MsgType.Request};   //生成消息头
        ans._msgHead = {id:msgID, type:messageMgr.MsgType.Answer};
        messageMgr.request_map.set(msgID, req);    //建立ID与消息的关联
        messageMgr.answer_map.set(msgID, ans);
    }
    //关联通知消息
    for(let msgName in messageMgr.NoticeMsgID)
    {
        let msgNotice = "Notice" + msgName;
        let msgID = messageMgr.NoticeMsgID[msgName];
        let notice = lz.msg[msgNotice];
        notice._msgHead = {id:msgID, type:messageMgr.MsgType.Notice};
        messageMgr.notice_map.set(msgID, notice);
    }

    console.log(messageMgr.request_map, messageMgr.answer_map, messageMgr.notice_map);
}

msg_process(msgMgr);  //关联消息
msgMgr.encode_msg(msgMgr.Msg.ReqHelloWorld, { id: 1999, str: "测试发送中文", opt: 0});  //编码一个消息
msgMgr.decode_msg();   //解码一个消息
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!