程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> chatofPomelo game-server解析

chatofPomelo game-server解析

編輯:C++入門知識

chatofPomelo game-server解析


chatOfPomelo是一個聊天室程序,筆者將對chat的服務端game-server進行分析
首先來看它的文件結構
這裡寫圖片描述
大的結構與hellopomelo一致,都有app,config,logs,node_modules,app.js.
有了hello的經驗,我們直奔主題config/servers.json
這裡配置著我們自定義的服務器配置

{
    development:{
        connector:[
             {id:connector-server-1, host:127.0.0.1, port:4050, clientPort: 3050, frontend: true}
         ],
        chat:[
             {id:chat-server-1, host:127.0.0.1, port:6050}
        ],
        gate:[
           {id: gate-server-1, host: 127.0.0.1, clientPort: 3014, frontend: true}
        ]
    },
    production:{
           connector:[
             {id:connector-server-1, host:127.0.0.1, port:4050, clientPort: 3050, frontend: true}
         ],
        chat:[
             {id:chat-server-1, host:127.0.0.1, port:6050}
        ],
        gate:[
           {id: gate-server-1, host: 127.0.0.1, clientPort: 3014, frontend: true}
        ]
  }
}

這裡看出來對於development和production環境都作了同樣的配置
這次的聊天室是由三個自定義的服務組成
gate,chat,connector
gate– 前端網關服務,用於最初與服務器的連接,並分配connector給客戶端
connector– 前端連接服務器,用於與客戶端保持websocket長連接,並轉發後端服務器處理的協議
chat– 後端聊天服務器,客戶端不能與它進行連接,它處理好的邏輯通過connector傳遞給客戶端
看看3個服務的目錄結構
這裡寫圖片描述
注意到gate,connector作為前端服務濃ky"http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcsy/vDx7a809BoYW5kbGVyzsS8/rzQPGJyIC8+DQq2+GNoYXS687bLt/7O8cb3LLK7vfbT0GhhbmRsZXK7udPQcmVtb3RlzsS8/rzQPGJyIC8+DQrU2nBvbWVsb9bQLNPDu6fX1Lao0uW1xGhhbmRsZXLOxLz+vNCx7cq+0qq0psDt0+u/zbuntsvBrL3Tz+C52LXEysK8/jxiciAvPg0KtvhyZW1vdGXOxLz+1PKx7cq+tKbA7bf+zvHG97XEcnBjtffTwzxiciAvPg0KaGFuZGxlcs7EvP680MDvtcTOxLz+u+Gxu9f3zqrX6bz+vNPU2C7H0rfFyOu1vdK7uPZyb3V0ZdbQLNLUsePT2r/Nu6e2y8/ycG9tZWxvx+vH82dhdGUuZ2F0ZUhhbmRsZXIucXVlcnlFbnRyedXi0fm1xMfrx/PKsSy74bG7vqvIt7aozru1vTxiciAvPg0KZ2F0ZS9oYW5kbGVyL2dhdGVIYW5kbGVyLmpz1tC1xHF1ZXJ5RW50cnm907/aPGJyIC8+DQq/tL+0Z2F0ZUhhbmRsZXIuanO1xLT6wus8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;"> module.exports = function(app) { // 導出handler類 return new Handler(app); }; var Handler = function(app) { this.app = app; }; var handler = Handler.prototype; handler.queryEntry = function(msg, session, next) { // 客戶端訪問gate的唯一接口 var uid = msg.uid; if(!uid) { next(null, { code: 500 }); return; } // get all connectors var connectors = this.app.getServersByType('connector'); if(!connectors || connectors.length === 0) { next(null, { code: 500 }); return; } // here we just start `ONE` connector server, so we return the connectors[0] var res = connectors[0]; // next是向客戶端返回消息 next(null, { code: 200, host: res.host, port: res.clientPort }); };

entryHandler.js(這個文件在connector目錄底下,不管它叫什麼名字,都是connector的模塊)

module.exports = function(app) {
    return new Handler(app);
};

var Handler = function(app) {
        this.app = app;
};

var handler = Handler.prototype;
// 客戶端訪問connector的入口
handler.enter = function(msg, session, next) {
    var self = this;
    var rid = msg.rid;
    var uid = msg.username + '*' + rid
    // sessionService是pomelo提供的默認服務,維護session信息
    var sessionService = self.app.get('sessionService');

    //duplicate log in
    if( !! sessionService.getByUid(uid)) {
        next(null, {
            code: 500,
            error: true
        });
        return;
    }
    session.bind(uid);
    session.set('rid', rid);
    session.push('rid', function(err) {
        if(err) {
            console.error('set rid for session service failed! error is : %j', err.stack);
        }
    });
    session.on('closed', onUserLeave.bind(null, self.app));

    //通過rpc調用把玩家加入到chat服務中,並返回玩家列表給客戶端
    self.app.rpc.chat.chatRemote.add(session, uid, self.app.get('serverId'), rid, true, function(users){
        next(null, {
            users:users
        });
    });
};
// 玩家關閉浏覽以後,通過rpc調用通知chatRemote把這個玩家刪除
var onUserLeave = function(app, session) {
    if(!session || !session.uid) {
        return;
    }
    app.rpc.chat.chatRemote.kick(session, session.uid, app.get('serverId'), session.get('rid'), null);
};

chat分2部分,一部分是handler,另一部分是remote
handler說明它要處理客戶端的信息
remote說明它要處理來自後端服務器的信息
由於我們的connecotr用到了chat的add以及kick,因此我們先看remote的內容
chatremote.js

module.exports = function(app) {
    return new ChatRemote(app);
};

var ChatRemote = function(app) {
    this.app = app;
    this.channelService = app.get('channelService'); // channel也是pomelo的默認服務器
};

// 把玩家添加到指定的channel中 uid玩家id sid服務器id name 頻道名 flag 頻道標識符
ChatRemote.prototype.add = function(uid, sid, name, flag, cb) {
    var channel = this.channelService.getChannel(name, flag);
    var username = uid.split('*')[0];
    var param = {
        route: 'onAdd',
        user: username
    };
    channel.pushMessage(param);

    if( !! channel) {
        channel.add(uid, sid);
    }
    cb(this.get(name, flag));
};

// 從channel中獲取玩家信息 name 頻道名 flag 頻道參數 返回玩家列表
ChatRemote.prototype.get = function(name, flag) {
    var users = [];
    var channel = this.channelService.getChannel(name, flag);
    if( !! channel) {
        users = channel.getMembers();
    }
    for(var i = 0; i < users.length; i++) {
        users[i] = users[i].split('*')[0];
    }
    return users;
};

//把玩家從channel中踢出
ChatRemote.prototype.kick = function(uid, sid, name) {
    var channel = this.channelService.getChannel(name, false);
    // leave channel
    if( !! channel) {
        channel.leave(uid, sid);
    }
    var username = uid.split('*')[0];
    var param = {
        route: 'onLeave',
        user: username
    };
    channel.pushMessage(param);
};

從代碼上看出都是與pomelo的內部組件打交道的方法,代碼簡單,不過這裡的所有發往客戶端的message都是通過channel來完成的.channel又是通過connector來完成,所以此服務器與客戶端並不直接相聯,都通過connector來中轉

最後是 chat.Handler,在pomelo中,handler都是處理客戶端消息的地方,不過由於chat不是前端服務器,這裡的send事件是由connector捕捉到以後route過來的,而不是客戶端直接與chat服務器進行的連接.

var chatRemote = require('../remote/chatRemote');

module.exports = function(app) {
    return new Handler(app);
};

var Handler = function(app) {
    this.app = app;
};

var handler = Handler.prototype;

// 處理收到的來自connecotr轉發的客戶端消息
handler.send = function(msg, session, next) {
    var rid = session.get('rid');  //獲取玩家的填的channel id
    var username = session.uid.split('*')[0];
    var channelService = this.app.get('channelService');
    var param = {
        msg: msg.content,
        from: username,
        target: msg.target
    };
    channel = channelService.getChannel(rid, false);  // 通過channelid獲取與此服務器對應的channel實例

    //the target is all users
    if(msg.target == '*') {
        channel.pushMessage('onChat', param);  // channel通過connector把消息發送到客戶端
    }   
    else { //the target is specific user
        var tuid = msg.target + '*' + rid;
        var tsid = channel.getMember(tuid)['sid'];
        channelService.pushMessageByUids('onChat', param, [{
            uid: tuid,
            sid: tsid
        }]);
    }
    next(null, {
        route: msg.route
    });
};

最後我們來看一下整個網絡結構圖
這裡寫圖片描述
gate是唯一的,起到大門的作用.客戶端首先發起連接是和gate產生的,由gate決定客戶端與哪個connecotr連接,gate可以起到負載均衡的作用
這裡可以有多個connector群.負責承載來自客戶端的連接,並與後端的chatroom進行交互.
這裡也可以有多個chatroom,每個chat可以對應到不同的connector.
後端的chatroom不與客戶端直接連接

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved