cocoscreator与nodejs网络基础通信(三)

这一章讲解cocoscreator与nodejs整合protobuf。

protobuf是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。

protobuf是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。

其实我觉得没有特殊的情况,cocoscreator与nodejs之间通信协议就用json就挺好的,方便,方便,方便。

本文使用protobuf地址是:protobuf链接

先讲解一下思路:

前面我们讲解了,收到数据,会把数据解析成消息号+消息体,消息体是一串二进制数据,要转换成json,所以我们需要一个proto来解析,要快速找到那个可以解析该数据的proto,我们需要将消息号和proto做一个映射关系。

下面来看服务器端的实现。

proto文件数据为:

syntax = "proto2";
package login;

message login_req {
    optional string     acc             = 1 [ default = "" ];
    optional string     pwd             = 2 [ default = "" ];
    optional int32      age             = 3 [ default = 0 ];
}

message login_res {
    optional bool       result          = 1 [ default = false ];
    optional int32      uid             = 2 [ default = 0 ];
    optional string     name            = 3 [ default = "" ];
}

syntax = "proto2"; //版本
package login; //包名

optional表明字段是可选的,如果是required,则该字段是必须的,repeated用在是数组的情况。

加载proto核心代码段

var protoroot = {};
var files = fs.readdirSync("./proto");//读取proto文件夹下的所有proto文件
//console.log(files);
for (var i = 0; i < files.length; i++) {//遍历所有文件
    var ext = path.extname(files[i]);//取出文件扩展名
    if (ext === ".proto") {//如果是proto文件
        //调用protobuf核心函数
        var proot = protobuf.loadSync("./proto/" + files[i]);
        for (var packagename in proot.nested) {
            for (var msgname in proot.nested[packagename].nested) {
                console.log(msgname);
                var msgid = PCONST[msgname];
                if (msgid != undefined) {
                    console.log(msgid);
                    //将proto解析器与消息号建立对应关系
                    protoroot[msgid] = proot.lookupType(msgname);
                }
            }
        }
    }
}

其中逻辑层代码有一些约定的写法,比如在const.js中

var CONST = module.exports;
CONST.SYS_MSG = {};
CONST.SYS_MSG.CONNECT = "_connect";
CONST.SYS_MSG.CLOSE = "_close";

CONST.LOGIC_MSG = {};
CONST.LOGIC_MSG.LOGIN_REQ = 1001;
CONST.LOGIC_MSG.LOGIN_RES = 2001;

CONST.PCONST = {};
CONST.PCONST["login_req"] = CONST.LOGIC_MSG.LOGIN_REQ;
CONST.PCONST["login_res"] = CONST.LOGIC_MSG.LOGIN_RES;

其中CONST.PCONST["login_req"]中的login_req与proto文件中的message login_req对应。

其中建立1001和login_req的关系,通过login_req与proto文件里message name同名则可建立映射关系。

​
//调用decode反序列化成json数据。
protoroot[msgid].decode(body_buffer)


//将格式为json的msg序列化成二进制数据
var data = this.protoroot[msgid].create(msg);
var bodyBuf = this.protoroot[msgid].encode(data).finish();

再看看客户端:

在protobufjs包的protobufjs\dist目录找到protobuf.js文件,稍加改动即可用于cocoscreator。如下

function fetch(filename, options, callback) {
    ......

    if (typeof cc !== "undefined") {//判断是否是cocos项目

        if (false) {//cc.sys.isNative
            var content = jsb.fileUtils.getStringFromFile(filename);
            callback(content === "" ? Error(filename + " not exits") : null, content);
        } else {
            //cc.log("cc.loader load 1 filename=" + filename);
            //这里可以加载一个url图片 : "Host"+filename
            // cc.loader.load(filename, function (error, result) {
            //     cc.log("error1=" + error + ",result = " + result + ",type=" + typeof result);
            //     // callback(null, result);
            // });
            //cc.log("cc.loader load 2");

            // 这里h5会去加载resources目录下的文件 : "resources/"+ filename
            // 这里filename一般不用指定扩展名,当然你也可以强制指定
            //cc.TextAsset,
            if (cc.path.extname(filename) === ".proto") {
                var temp = filename.split(".");
                filename = temp[0];
            }
            cc.loader.loadRes(filename, function (error, result) {
                cc.log("loadRes : " + filename);
                //cc.log("error2=" + error + ",result = " + result + ",type=" + typeof result);
                if (error) {
                    callback(Error("status " + error))
                } else {
                    callback(null, result.toString());
                }
            });
            //cc.log("cc.loader load 3");
        }

        return;
    }

    ......
}

伪装成nodejs项目即可。

客户端使用的方法基本和后端类似。建立msgid与proto解析的映射关系。

如下:

var CONST = cc;
CONST.MSG = {};
CONST.PTOTO = {};
CONST.PTOTO["login"] = {};
CONST.MSG.LOGIN_REQ = 1001;
CONST.PTOTO["login"][CONST.MSG.LOGIN_REQ] = "login_req";
CONST.MSG.LOGIN_RES = 2001;
CONST.PTOTO["login"][CONST.MSG.LOGIN_RES] = "login_res";

exports.loadProto = function (cb) {
    var protoroot = {};
    var count = 0;
    var _loadProto = function (name, file_name) {
        try {
            protobuf.load(file_name, function (err, root) {
                cc.log("protobuf.load return " + file_name);
                if (err)
                    throw err;
                try {
                    var objs = cc.PTOTO[name];
                    for (var msgid in objs) {
                        cc.log(msgid);
                        protoroot[msgid] = root.lookupType(objs[msgid]);
                    }
                    count--;
                    done();
                }
                catch (err) {
                    throw err;
                }
            });
        }
        catch (err) {
            throw err;
        }
    };
    var done = function () {
        if (count === 0)
            cb(protoroot);
    };
    var arr = Object.keys(cc.PTOTO);
    if (arr.length == 0) done();

    //遍历proto文件
    for (var name in cc.PTOTO) {
        var filename = "proto/" + name + "";
        cc.log("load " + filename);
        count++;
        _loadProto(name, filename);
    }
};

其中不同点在于CONST.PTOTO["login"]中的login是proto文件名。通过遍历CONST.PTOTO来遍历所有proto文件来建立映射关系,至于为什么这么做,读者可以自己琢磨一下。高手忽略本文。

//数据序列化
var message = msgroot.create(msg);
var buf = msgroot.encode(message).finish();

//数据反序列化
var msg_root = cc.protoRoot[msgid];
var msg = msg_root.decode(msg_buf);

总结一下,服务器端采用的同步加载的方式,客户端则使用的异步方式。逻辑主要是建立消息号和proto解析方法的对应关系,里面有一些常量约定的写法,为的就是方便使用protobuf。至于proto使用的一些参数和代码没有给大家详细讲解。大家可下载完整代码参考。谢谢支持。客户端代码地址服务器端代码地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值