mysql4.redis模塊-->redis框架劃分1.webserver?..."/>
溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

棋牌游戲服務器設計(1)

發(fā)布時間:2020-07-04 13:18:23 來源:網絡 閱讀:1322 作者:超級極客 欄目:開發(fā)技術

一、項目劃分

框架依賴的模塊

1.高性能webserver--express模塊 熱更新 語音對話

2.websocket模塊--ws

3.mysql模塊-->mysql

4.redis模塊-->redis


框架劃分

1.webserver作用就是用來上傳下載,獲取配置信息,更新等.

通過web接入第三方的sdk。

2.gateway網關服務器,

(1)用來接收所有用戶的長連接,轉發(fā)用戶請求

(2)連接游戲服務器轉發(fā)服務器回應

(3)安全防護,過濾非法的數據包隔離游戲服務器免受客戶***.

3.用戶中心服務器: 用來管理這個用戶的賬號信息.這個用戶信息

管理號,可以在進行這個平臺的其他游戲.

4.系統(tǒng)服務: 處理用戶和系統(tǒng)進行交互:每日登錄,郵件,兌換東西等等.

比如你要做一個活動,這個就是用戶和服務器單獨交互.不會涉及其他用戶.

5.游戲服務器:他涉及到多個用戶之間的交互,處理不同游戲的服務.


每個服務又是一個進程,


數據庫劃分

1.后臺數據庫,分為兩個.第一個數據庫用來存放用戶數據,大家公用的,

第二個是每個游戲都有一個游戲數據庫。

2.為了訪問速度的提高,把常用的數據緩存到redis服務器,

redis緩沖中心:也是兩個:用戶redis,和游戲數據redis.

3.數據庫是所有進程都公用的.



3rd/utils/netbus模塊

1.3rd存放第三方的js代碼庫

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

3.netbus模塊,為所有長連接服務器所公用,支持websocket

TCP socket 二進制和json協(xié)議。




二、日志 TCPsocket和websocket模塊封裝支持


Log日志代碼

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

2.log寫日志必須是異步的

3.log日志能夠方便的定向到對應的服務器里去

4.log日志分登記和顏色

5.調試時全部打印到標準的輸出文件,上線時在輸出在文件

6.創(chuàng)建一個output文件夾存放輸出的日志.

7.日志分級,如一般信息,警告信息,錯誤信息.

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


驗證數據的合法性

1.tcp socket收到的數據必須是Buffer類型

2.ws socket json數據協(xié)議下收到的類型必須是字符串

3.ws socket buf數據協(xié)議下收到的類型必須是buffer



三、協(xié)議管理模塊

1.協(xié)議規(guī)定是: 服務號,命令號,數據部分

2.提供協(xié)議解碼 cmd[0]服務號,cmd[1]命令號,cmd[2]body三個部分

3.提供協(xié)議編碼函數轉json字符 或者 buff二進制(2字節(jié)2字節(jié)+body);

4提供協(xié)議服務端buf×××注冊函數

5.同時支持json和二進制,通過客戶端連接自己選擇

6.協(xié)議加密和解密也可以加入到這個模塊

先編碼在加密 —— 先解密在解碼

一般只需要支持一種協(xié)議即可.



四、netbus服務管理模塊

1.當netbus收到數據包的時候,需要把包分發(fā)給對應的服務來進行處理

2.service_manager(mg管理)

3.所有服務的管理模塊,所有的服務都注冊到這里

4.netbus收到數據,玩家掉線等,都進入它,通知對應的服務

5.提供服務模塊注冊函數,編寫模板服務編寫

6.轉發(fā)到對應的服務后,使用decode_cmd 加密命令

7.告訴所有的service鏈接丟失





五、creator支持websocket_http支持buf和json協(xié)議

1.creator使用websocket和服務器進行聯(lián)機,因為本身creator

本身是h6的,所以

2.ArrayBuffer.DataView,utf8,string字節(jié)長度,DataView讀/寫字符串

ArrayBuffer沒有Buffer模塊這么多接口,比如readUInt16LE這些.

這個時候就需要借助DataView.

棋牌游戲服務器設計(1)


他有一個參數,這個參數是可選的。

如果為 false 或未定義,則寫入big-endian(大尾) 值;

否則應寫入 little-endia(小尾) 值。

        var buf = new ArrayBuffer(10);
        //無法直接操作數據 借助DataView
        var dataview = new DataView(buf);
        //在第0個字節(jié)寫入100 8一個字節(jié)沒有大小尾
        dataview.setUint8(0,100);
        var value = dataview.getUint8(0);
        console.log(value);


棋牌游戲服務器設計(1)


而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,      //協(xié)議類型
    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;
    },

}



//選擇啟動協(xié)議
//使用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方法初始化
        //必須在發(fā)送請求前調用
        //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的請求狀態(tài) 當XMLHttpRequest
        //初次創(chuàng)建時,這個屬性是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
                //請求狀態(tài) 和 狀態(tài)碼
                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){
            //在發(fā)送到服務器之前,所有字符都會進行編碼
            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
                //請求狀態(tài) 和 狀態(tài)碼
                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;








向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI