您的位置:首页 > Web前端 > Node.js

在node中使用protobuf.js

2017-02-06 18:15 567 查看
本文基于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();   //解码一个消息
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: