chatofpomelo源碼分析(二)——聊天

上一篇簡要的分析客戶端登陸的過程。當用戶登陸成功後,聊天的過程又是如何進行的呢?下面就來分析聊天時,客戶端與服務器端的交互過程。

客戶端

我們先來看看下,聊天發送消息的過程。當用戶在文本框內輸入文字,並回車就可以發送消息了

   1: $("#entry").keypress(function (e) {
   2:var route = "chat.chatHandler.send";
   3:var target = $("#usersList").val();
   4:if (e.keyCode != 13 /* Return */) return;
   5:var msg = $("#entry").attr("value").replace("\n", "");
   6:if (!util.isBlank(msg)) {
   7:             pomelo.request(route, {//route = "chat.chatHandler.send"
   8:                 rid: rid,
   9:                 content: msg,
  10:                 from: username,
  11:                 target: target
  12:             }, function (data) {
  13:                 $("#entry").attr("value", ""); // clear the entry field.
  14:if (target != '*' && target != username) {
  15:                     addMessage(username, target, msg);
  16:                     $("#chatHistory").show();
  17:                 }
  18:             });
  19:         }
  20:     });

#1:entry就是聊天文本框的id了,當焦點在entry(就用id來代表控件了),每次按下鍵盤都會觸發keypress()方法,方法接受一個事件e

#2:route,決定客戶端向服務器端的哪個方法發送請求。

#3:target,entry的上方有個名爲users的下拉框,target就是下拉框的值了,決定用戶向誰發送消息。

#4-#5:對輸入的字符判斷,如果不是回車就返回,如果是回車就將entry中的換行符替換成空字符串

#6:util是client.js定義的一個對象,裏面包含了一些對字符的處理方法,其中isBlank()是判斷字符串是否是空字符串

#7:如果不是空字符串,就將這條消息發送給服務器,route就是#2所定義的服務器的處理方法 chat.chatHandelr.send

#8-#11:客戶端將用戶所在的channel,發送的消息了,用戶名以及發送消息的對象封裝成對象,發送給服務器

#12:定義回調函數,處理服務器返回的結果對象data

#13:清空entry

#14:根據發送的對象判斷是否將發送的添加到聊天記錄中

#15:在聊天記錄(id=chatHistory)顯示

服務器端

接下來,在看服務器端收到客戶端發送的請求是怎麼處理的。打開chatofpomelo\game-server\app\servers\chat\handler\chatHandler.js

找到handler.send

   1: handler.send = function(msg, session, next) {
   2:var rid = session.get('rid');
   3:var username = session.uid.split('*')[0];
   4:var channelService = this.app.get('channelService');
   5:var param = {
   6:         route: 'onChat',
   7:         msg: msg.content,
   8:         from: username,
   9:         target: msg.target
  10:     };
  11:     channel = channelService.getChannel(rid, false);
  12:
  13://the target is all users
  14:if(msg.target == '*') {
  15:         channel.pushMessage(param);
  16:     }
  17://the target is specific user
  18:else {
  19:var tuid = msg.target + '*' + rid;
  20:var tsid = channel.getMember(tuid)['sid'];
  21:         channelService.pushMessageByUids(param, [{
  22:             uid: tuid,
  23:             sid: tsid
  24:         }]);
  25:     }
  26:     next(null, {
  27:         route: msg.route
  28:     });
  29: };

#1:解釋下參數 msg就是客戶端發送的對象,session就是服務器端與當前用戶的會話,next相當於把結果發送給客戶端 PS:next的真正功能我也描述不清,還請各位指點。

#2-#3:從session中取得roomID(rid)和用戶名usernmae,chatofpomelo\game-server\app\servers\connector\handler\entryHandler.js的enter()方法有對於session的設置

#4:獲取ChannelService(管理Channel)

#5-#10:把發送的信息,用戶名和發送信息的對象以及route這裏的route:onChat由服務器端定義的,客戶端監聽。每個客戶端都會監聽onXXX事件,監聽服務器發送的消息。這樣用戶發送的消息才能由服務器發送給其他用戶。

#11:根據用戶發送的rid,獲取對應的channel

#14-#25:判斷髮送對象,是廣播還是發送給特定用戶。

#26-28:將結果返回給客戶端

其實,分析這麼多代碼,前面我寫的很詳細,後面就寫的簡略了。分析完後可以發現,其實交互部分情況類似,只要弄懂了其中一部分,其餘的也就好懂了。

PS:到此,這個demo的雛形就完成了,本來到這結束了……確實,如果你的servers.json裏只有一個chat服務器,那麼一切流程都可正常運行。但是,如果不只一個chat服務器,那肯定會遇到問題的,是不是信息發不出去,在看服務器端,報錯了!!

是不是channel爲undefined?看上面的第11行

   1: channel = channelService.getChannel(rid, false);

很抱歉,channelService裏並沒有channel,你一定很奇怪,當用戶登錄時,不是創建了channel了嗎,怎麼會沒有呢?

爲了驗證,我把add和send時的app打印出來,對比


發現確實創建的channel沒有了,到底是怎麼回事?再比較,就會發現還有一個不同之處。


你會發現,登陸和聊天時所在的服務器不一樣,難怪channel消失。

其實,聊天和登陸一樣,用戶在發送消息時,看上面客戶端代碼的第8行和第10行,會發現客戶端不只會發送消息,還會把用戶名username和rid同時發送給服務器端。服務器端會根據username和rid,實際上只有rid,判斷該用戶是位於哪臺chat-server上,這就是爲什麼game-server/app.js會有這幾行代碼

   1: app.configure('production|development', function () {
   2:// route configures
   3:     app.route('chat', routeUtil.chat);//routes的chat屬性對應routeUtil.chat()方法 
   4:     app.filter(pomelo.timeout());
   5: });

#2:當服務器類型是chat,就會把路由到routeUtil.chat方法。

然後在進入該方法,看看是怎麼判斷用戶屬於哪個路由器的。

   1: exp.chat = function(session, msg, app, cb) {
   2:  
   3:     console.log("uid = " + session.uid + " rid = " + session.get("rid"));
   4:var chatServers = app.getServersByType('chat');//根據類型 獲取服務器列表
   5:  
   6:if(!chatServers || chatServers.length === 0) {//如果服務器列表不存在或爲空,則調用回調函數cb,將錯誤傳給該回調
   7:         cb(new Error('can not find chat servers.'));
   8:return;
   9:     }
  10:  
  11:var res = dispatcher.dispatch(session.get('rid'), chatServers);//通過rid獲得具體的chat服務器
  12:     console.log("chat服務器:" + res.id);
  13:     cb(null, res.id);
  14: };

#11:之前的代碼,相信大家都明白了。我們直接看11行代碼,是不是很眼熟?dispatcher好像見過!還記得之前客戶端連接gate服務器,然後由gate服務器分配connector服務器,再返回其host和clientPort嗎?不錯,這裏同樣是調用該方法,只不過connectors改成了chatServers。這樣根據傳入的rid,就可判斷用戶當初登陸的那個chat-server,這樣也就能找到對應的channel

疑問:雖然解決了這一問題,但是還是不明白如果不添加對chat的路由,即沒有這行代碼:

   1: app.route('chat', routeUtil.chat);

是怎麼分配chat-server,是隨機分配,還是自增分配?

還有就是這句代碼是何時被調用的。

希望各位能夠指點一下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章