上一篇ChatofPomelo簡析之一——用戶登陸分析客戶端登錄的過程。當用戶登錄成功後,聊天又是個什麼過程呢?下面就來分析聊天時,客戶端與服務器端的交互過程。git
咱們先來看看下,聊天發送消息的過程。當用戶在文本框內輸入文字,並回車就能夠發送消息了github
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()方法,方法接受一個事件ejson
#2:route,決定客戶端向服務器端的哪一個方法發送請求。服務器
#3:target,entry的上方有個名爲users的下拉框,target就是下拉框的值了,決定用戶向誰發送消息。session
#4-#5:對輸入的字符判斷,若是不是回車就返回,若是是回車就將entry中的換行符替換成空字符串app
#6:util是client.js定義的一個對象,裏面包含了一些對字符的處理方法,其中isBlank()是判斷字符串是不是空字符串ide
#7:若是不是空字符串,就將這條消息發送給服務器,route就是#2所定義的服務器的處理方法 chat.chatHandelr.send函數
#8-#11:客戶端將用戶所在的channel,發送的消息了,用戶名以及發送消息的對象封裝成對象,發送給服務器ui
#12:定義回調函數,處理服務器返回的結果對象datathis
#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,是隨機分配,仍是自增分配?
還有就是這句代碼是什麼時候被調用的。
但願各位可以指點一下。
參考資料: