美文网首页我爱编程
转:棋牌游戏服务器设计(1)

转:棋牌游戏服务器设计(1)

作者: Designleiou | 来源:发表于2018-06-21 10:43 被阅读0次

    一、项目划分

    框架依赖的模块

    1.高性能webserver--express模块 热更新 语音对话

    2.websocket模块--ws

    3.mysql模块-->mysql

    4.redis模块-->redis

    框架划分

    1.webserver作用就是用来上传下载,获取配置信息,更新等.

    通过web接入第三方的sdk。

    2.gateway网关服务器,

    (1)用来接收所有用户的长连接,转发用户请求

    (2)连接游戏服务器转发服务器回应

    (3)安全防护,过滤非法的数据包隔离游戏服务器免受客户攻击.

    3.用户中心服务器: 用来管理这个用户的账号信息.这个用户信息

    管理号,可以在进行这个平台的其他游戏.

    4.系统服务: 处理用户和系统进行交互:每日登录,邮件,兑换东西等等.

    比如你要做一个活动,这个就是用户和服务器单独交互.不会涉及其他用户.

    5.游戏服务器:他涉及到多个用户之间的交互,处理不同游戏的服务.

    每个服务又是一个进程,

    数据库划分

    1.后台数据库,分为两个.第一个数据库用来存放用户数据,大家公用的,

    第二个是每个游戏都有一个游戏数据库。

    2.为了访问速度的提高,把常用的数据缓存到redis服务器,

    redis缓冲中心:也是两个:用户redis,和游戏数据redis.

    3.数据库是所有进程都公用的.

    3rd/utils/netbus模块

    1.3rd存放第三方的js代码库

    2.utils存放所有的公共模块

    3.netbus模块,为所有长连接服务器所公用,支持websocket

    TCP socket 二进制和json协议。

    二、日志 TCPsocket和websocket模块封装支持

    Log日志代码

    1.log日志是重要的服务器手段

    2.log写日志必须是异步的

    3.log日志能够方便的定向到对应的服务器里去

    4.log日志分登记和颜色

    5.调试时全部打印到标准的输出文件,上线时在输出在文件

    6.创建一个output文件夹存放输出的日志.

    7.日志分级,如一般信息,警告信息,错误信息.

    8.把重要信息保存到log,比如你充值了10元.

    验证数据的合法性

    1.tcp socket收到的数据必须是Buffer类型

    2.ws socket json数据协议下收到的类型必须是字符串

    3.ws socket buf数据协议下收到的类型必须是buffer

    三、协议管理模块

    1.协议规定是: 服务号,命令号,数据部分

    2.提供协议解码 cmd[0]服务号,cmd[1]命令号,cmd[2]body三个部分

    3.提供协议编码函数转json字符 或者 buff二进制(2字节2字节+body);

    4提供协议服务端buf解码器注册函数

    5.同时支持json和二进制,通过客户端连接自己选择

    6.协议加密和解密也可以加入到这个模块

    先编码在加密 —— 先解密在解码

    一般只需要支持一种协议即可.

    四、netbus服务管理模块

    1.当netbus收到数据包的时候,需要把包分发给对应的服务来进行处理

    2.service_manager(mg管理)

    3.所有服务的管理模块,所有的服务都注册到这里

    4.netbus收到数据,玩家掉线等,都进入它,通知对应的服务

    5.提供服务模块注册函数,编写模板服务编写

    6.转发到对应的服务后,使用decode_cmd 加密命令

    7.告诉所有的service链接丢失

    五、creator支持websocket_http支持buf和json协议

    1.creator使用websocket和服务器进行联机,因为本身creator

    本身是h5的,所以

    2.ArrayBuffer.DataView,utf8,string字节长度,DataView读/写字符串

    ArrayBuffer没有Buffer模块这么多接口,比如readUInt16LE这些.

    这个时候就需要借助DataView.

    他有一个参数,这个参数是可选的。

    如果为 false 或未定义,则写入big-endian(大尾) 值;

    否则应写入 little-endia(小尾) 值。

            var buf = new ArrayBuffer(10);

            //无法直接操作数据 借助DataView

            var dataview = new DataView(buf);

            //在第0个字节写入100 8一个字节没有大小尾

            dataview.setUint8(0,100);

            var value = dataview.getUint8(0);

            console.log(value);

    而DataView只能处理数,没办法处理字符串.需要扩展DataView

    //写入utf8字符串

    DataView.prototype.write_utf8 = function(offset,str){

        var now = offset;

        var dataview = this;

        for (var i = 0; i < str.length; i++) {

            var charcode = str.charCodeAt(i);

            if (charcode < 0x80) {

                dataview.setUint8(now, charcode);

                now ++;

            }

            else if (charcode < 0x800) {

                dataview.setUint8(now, (0xc0 | (charcode >> 6)));

                now ++;

                dataview.setUint8(now, 0x80 | (charcode & 0x3f));

                now ++;

            }

            else if (charcode < 0xd800 || charcode >= 0xe000) {

                dataview.setUint8(now, 0xe0 | (charcode >> 12));

                now ++;

                dataview.setUint8(now, 0x80 | ((charcode>>6) & 0x3f));

                now ++;

                dataview.setUint8(now, 0x80 | (charcode & 0x3f));

                now ++;

            }

            // surrogate pair

            else {

                i ++;

                charcode = 0x10000 + (((charcode & 0x3ff)<<10)

                          | (str.charCodeAt(i) & 0x3ff));

                dataview.setUint8(now, 0xf0 | (charcode >>18));

                now ++;

                dataview.setUint8(now, 0x80 | ((charcode>>12) & 0x3f));

                now ++;

                dataview.setUint8(now, 0x80 | ((charcode>>6) & 0x3f));

                now ++;

                dataview.setUint8(now, 0x80 | (charcode & 0x3f));

                now ++;

            }

        }

    }

    //读取utf8字符串

    DataView.prototype.read_utf8 = function(offset,byte_length){

        var out,i,len,c;

        var char2,char3;

        var dataview = this;

        //输出

        out = "";

        len = byte_length;

        i = offset;

        while(i < len){

            c = dataview.getUint8(i);

            i++;

            //这个字符串右移4位 判断这个位的值

            switch(c >> 4)

            {

            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:

                //Unicode 编码转为一个字符

                //例如他是十进制65 则会转成ACSLL 'A'

                out += String.fromCharCode(i);

            break;

            case 12: case 13:

                char2 = array[i++];

                out += String.fromCharCode(((c & 0x1F)<<6) | (char2 & 0x3F));

            break;

            case 14:

                char2 = dataview.getUint8(i);

                i++;

                char3 = dataview.getUint8(i);

                i++;

                out += String.fromCharCode(((c & 0x0F)<<12)|

                    ((char2 & 0x3F) << 6) |

                    ((char3 & 0x3F) << 0));

            break;

            }//end switch

        }

    }

    封装websocket模块

    //websocket模块封装

    var proto = require("proto_mgr");

    console.log("proto:",proto);

    var websocket = {

        sock: null,

        serivces_handler: null,  //当有消息来的时候回调函数

        proto_type: 0,      //协议类型

        is_commected: false,//是否连接

        _on_opened: function(event){

            console.log("ws connect server success!");

            this.is_commected = true;

        },

        _on_recv_data: function(strbufdata){

            if(!this.serivces_handler){

                console.log("not find serivces_handler");

                return;

            }

            //获取命令

            var cmd = proto.decode_cmd(this.proto_type,strbufdata);

            if(!cmd){

                console.log("websocket.js(25) cmd invaild!");

                return;

            }

            var stype = cmd[0];

            if(this.serivces_handler[stype]){

                this.serivces_handler[stype](cmd[0],cmd[1],cmd[2]);

            }

        },

        _on_socket_close: function(event){

            if(this.sock){

                this.close();

            }

        },

        _on_socket_err: function(event){

            this.close();

        },

        connect: function(url,proto_type){

            this.sock = new WebSocket(url);

            this.sock.onopen = this._on_opened.bind(this);

            this.sock.onmessage = this._on_recv_data.bind(this);

            this.sock.onclose = this._on_socket_close.bind(this);

            this.sock.onerror = this._on_socket_err.bind(this);

            this.proto_type = proto_type;

        },

        send_cmd: function(stype,ctype,body){

            if(!this.sock || !this.is_commected){

                console.log("send commind error!");

                return;

            }

            var buf = proto.encode_cmd(this.proto_type,stype,ctype,body);

            this.sock.send(buf);

        },

        close: function(){

            this.is_commected = false;

            if(this.sock !== null){

                this.sock.close();

                this.sock = null;

            }

        },

        regist_services_handler: function(serivces_handler){

            this.serivces_handler = serivces_handler;

        },

    }

    //选择启动协议

    //使用json 连接到服务

    //websocket.connect("ws://127.0.0.1:6081/ws",proto.PROTO_JSON);

    //使用二进制连接到服务

    //websocket.connect("ws://127.0.0.1:6083/ws",proto.PROTO_BUF);

    module.exports = websocket;

    封装http模块

    //http 模块

    var http = {

        //get请求 用于网页 文本

        get: function(url, path, params,callback){

            //获取XMLHTpRequest实例

            var xhr = cc.loader.getXMLHttpRequest();

            xhr.timeout = 5000;

            var requestURL = url + path;

            //添加变量

            if(params){

                requestURL = requestURL + "?" + params;

            }

            //http或https请求必须通过open方法初始化

            //必须在发送请求前调用

            //1:请求方法,2url,3ture就是异步请求 false阻塞

            //4用户名 5密码

            xhr.open("GET",requestURL,true);

            //true模拟器,手机  false web

            if(cc.sys.isNative){

                //设置请求头 信息

                xhr.setRequestHeader("Accept-Encoding","gzip,deflate","text/html;charset=UTF-8");

            }

            //readystate 是http的请求状态 当XMLHttpRequest

            //初次创建时,这个属性是0,直到接收到完整的HTTP响应

            //这个值增加到 4。

            //1是open方法被调用,send方法未调用

            //2是send方法被调用,HTTP请求到达web服务器

            //3是所有响应头部都已经接收到。响应体开始接收但未完成。

            //4是HTTP响应已经完成接收

            xhr.onreadystatechange = function(){

                if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){

                    //输出响应长度 和 正文

                    console.log("http res("+xhr.responseText.length+"):"+xhr.responseText);

                    try{

                        var ret = xhr.responseText;

                        if(callback !== null){

                            //把响应信息 传给回调函数

                            callback(null,ret);

                        }

                        return;

                    }catch(e){

                        //错误处理 把错误信息传给回调

                        callback(e,null);

                    }//end catch

                }//end if

                else{

                    //这里就是请求错误 传给callback

                    //请求状态 和 状态码

                    callback(xhr.readyState+":"+xhr.status,null);

                }//end else

            };

            xhr.send();

            return xhr;

        },

        //用于上传 body就是上传体

        post: function(url,path,params,body,callback){

            var xhr = cc.loader.getXMLHttpRequest();

            xhr.timeout = 5000;

            var requestURL = url + path;

            if(params){

                requestURL = requestURL + "?" + params;

            }

            xhr.open("POST",requestURL,true);

            if(cc.sys.isNative){

                xhr.setRequestHeader("Accept-Encoding","gzip,deflate","text/html;charset=UTF-8");

            }   

            //判断是否有body

            if(body){

                //在发送到服务器之前,所有字符都会进行编码

                xhr.setRequestHeader("Content-Type","application/x-www-form=urlencoded");

                //长度

                xhr.setRequestHeader("Content-Length",body.length);

            }   

            xhr.onreadystatechange = function(){

                if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){

                    try{

                        var ret = xhr.responseText;

                        if(callback !== null){

                            //把响应信息 传给回调函数

                            callback(null,ret);

                        }

                        return;

                    }catch(e){

                        //错误处理 把错误信息传给回调

                        callback(e,null);

                    }//end catch

                }//end if

                else{

                    //这里就是请求错误 传给callback

                    //请求状态 和 状态码

                    callback(xhr.readyState+":"+xhr.status,null);

                }//end else

            };   

            if(body){

                xhr.sned(body);

            }

            return xhr;

        },

        //用于下载 文件 二进制文件

        download: function(url,path,params,callback){

            var xhr = cc.loader.getXMLHttpRequest();

            xhr.timeout = 5000;

            var requestURL = url + path;

            if(params){

                requestURL = requestURL + "?" + params;

            }

            //响应类型

            xhr.responseType = "arraybuffer";

            xhr.open("GET",requestURL,true);

            if(cc.sys.isNative){

                xhr.setRequestHeader("Accept-Encoding","gzip,deflate","text/html;charset=UTF-8");

            }

            xhr.onreadystatechange = function(){

                if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300)){   

                    var buffer = xhr.response;

                    var dataview = new DataView(buffer);

                    //8 位无符号整数值的类型化数组

                    var ints = new Uint8Array(buffer.byteLength);

                    for(var i = 0;i < ints.length; i++){

                        //获取response 二进制数据

                        ints[i] = dataview.getUint8(i);

                    }

                    callback(null,ints);

                }else{

                    callback(xhr.readyState+":"+xhr.status,null);

                }//end else

            };

            xhr.send();

            return xhr;

        },

    };

    module.exports = http;

    相关文章

      网友评论

        本文标题:转:棋牌游戏服务器设计(1)

        本文链接:https://www.haomeiwen.com/subject/zdvfyftx.html