// servers.json { "development": { "connector": [{ "id": "connector-server-1", "host": "127.0.0.1", "port": 4050, "clientPort": 3050, "frontend": true }, { "id": "connector-server-2", "host": "127.0.0.1", "port": 4051, "clientPort": 3051, "frontend": true }, { "id": "connector-server-3", "host": "127.0.0.1", "port": 4052, "clientPort": 3052, "frontend": true }], "chat": [{ "id": "chat-server-1", "host": "127.0.0.1", "port": 6050 }, { "id": "chat-server-2", "host": "127.0.0.1", "port": 6051 }, { "id": "chat-server-3", "host": "127.0.0.1", "port": 6052 }], "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 }, { "id": "connector-server-2", "host": "127.0.0.1", "port": 4051, "clientPort": 3051, "frontend": true }, { "id": "connector-server-3", "host": "127.0.0.1", "port": 4052, "clientPort": 3052, "frontend": true }], "chat": [{ "id": "chat-server-1", "host": "127.0.0.1", "port": 6050 }, { "id": "chat-server-2", "host": "127.0.0.1", "port": 6051 }, { "id": "chat-server-3", "host": "127.0.0.1", "port": 6052 }], "gate": [{ "id": "gate-server-1", "host": "127.0.0.1", "clientPort": 3014, "frontend": true }] } }
// cocos2d-js var pomeloChat = function() { var pomelo = window.pomelo; var route = 'gate.gateHandler.queryEntry'; var uid = "uid"; var rid = "rid"; var username = "username"; // 请求连接gate服务器 pomelo.init({ host: "192.168.33.192", port: 3014, log: true }, function() { // 连接成功之后,向gate服务器请求ip和port pomelo.request(route, { uid: uid }, function(data) { // 断开与gate服务器之间的连接 pomelo.disconnect(); // 使用gate服务器返回的ip和port请求连接connector服务器 pomelo.init({ host: data.host, port: data.port, log: true }, function() { // 连接成功之后,向connector服务器发送登录请求 var route = "connector.entryHandler.enter"; pomelo.request(route, { username: username, rid: rid }, function(data) { // 登录成功之后向聊天服务器发送聊天内容 cc.log(JSON.stringify(data)); chatSend(); }); }); }); // 客户端接收广播消息,并将消息并显示即可。 pomelo.on('onChat', function(data) { cc.log(data.from, data.target, data.msg); }); }); function chatSend() { var route = "chat.chatHandler.send"; var target = "*"; var msg = "msg" pomelo.request(route, { rid: rid, content: msg, from: username, target: target }, function(data) { cc.log(JSON.stringify(data)); }); }; }
// -------------------------------------------------------------------------------- // app.js // -------------------------------------------------------------------------------- var pomelo = require('pomelo'); var routeUtil = require('./app/util/routeUtil'); /** * Init app for client. */ var app = pomelo.createApp(); app.set('name', 'chatofpomelo-websocket'); // app configuration app.configure('production|development', 'connector', function() { app.set('connectorConfig', { connector: pomelo.connectors.hybridconnector, heartbeat: 3, useDict: true, useProtobuf: true }); }); app.configure('production|development', 'gate', function() { app.set('connectorConfig', { connector: pomelo.connectors.hybridconnector, useProtobuf: true }); }); // app configure app.configure('production|development', function() { // route configures app.route('chat', routeUtil.chat); // filter configures app.filter(pomelo.timeout()); }); // start app app.start(); process.on('uncaughtException', function(err) { console.error(' Caught exception: ' + err.stack); });
// -------------------------------------------------------------------------------- // routeUtil.js // -------------------------------------------------------------------------------- var exp = module.exports; var dispatcher = require('./dispatcher'); exp.chat = function(session, msg, app, cb) { var chatServers = app.getServersByType('chat'); if (!chatServers || chatServers.length === 0) { cb(new Error('can not find chat servers.')); return; } var res = dispatcher.dispatch(session.get('rid'), chatServers); cb(null, res.id); };
// -------------------------------------------------------------------------------- // gateHandler.js // -------------------------------------------------------------------------------- var dispatcher = require('../../../util/dispatcher'); module.exports = function(app) { return new Handler(app); }; var Handler = function(app) { this.app = app; }; var handler = Handler.prototype; /** * Gate handler that dispatch user to connectors. * * @param {Object} msg message from client * @param {Object} session * @param {Function} next next stemp callback * */ // 入口函数 handler.queryEntry = function(msg, session, next) { var uid = msg.uid; if (!uid) { next(null, { code: 500 }); return; } // 获得所有的connectors // get all connectors var connectors = this.app.getServersByType('connector'); if (!connectors || connectors.length === 0) { next(null, { code: 500 }); return; } // 从connectors中分配一个connector // select connector var res = dispatcher.dispatch(uid, connectors); // 将分配的connector的ip和端口返回给客户端 next(null, { code: 200, host: res.host, port: res.clientPort }); };
// -------------------------------------------------------------------------------- // dispatcher.js // -------------------------------------------------------------------------------- var crc = require('crc'); // 根据用户uid对总的connector取模,作为下标返回对应的connector module.exports.dispatch = function(uid, connectors) { var index = Math.abs(crc.crc32(uid)) % connectors.length; return connectors[index]; }; // -------------------------------------------------------------------------------- // connector.js // -------------------------------------------------------------------------------- module.exports = function(app) { return new Handler(app); }; var Handler = function(app) { this.app = app; }; var handler = Handler.prototype; /** * New client entry chat server. * * @param {Object} msg request message * @param {Object} session current session object * @param {Function} next next stemp callback * @return {Void} */ handler.enter = function(msg, session, next) { var self = this; var rid = msg.rid; var uid = msg.username + '*' + rid // 获得一个session var sessionService = self.app.get('sessionService'); // 重复登录 //duplicate log in if (!!sessionService.getByUid(uid)) { next(null, { code: 500, error: true }); return; } // 用户进入聊天室后,服务器端首先需要完成用户的session注册 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方法将用户加入到相应的channel中; // 同时在rpc方法中,服务器端需要将该用户的上线消息广播给其他用户, // 最后服务器端向客户端返回当前channel中的用户列表信息。 //put user into channel self.app.rpc.chat.chatRemote.add(session, uid, self.app.get('serverId'), rid, true, function(users) { next(null, { users: users }); }); }; /** * User log out handler * * @param {Object} app current application * @param {Object} session current session object * */ // 用户在退出聊天室时,必须完成一些清理工作。 // 在session断开连接时,通过rpc调用将用户从channel中移除。 // 在用户退出前,还需要将自己下线的消息广播给所有其他用户。 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); };
// -------------------------------------------------------------------------------- // ChatRemote.js // -------------------------------------------------------------------------------- module.exports = function(app) { return new ChatRemote(app); }; var ChatRemote = function(app) { this.app = app; this.channelService = app.get('channelService'); }; /** * Add user into chat channel. * * @param {String} uid unique id for user * @param {String} sid server id * @param {String} name channel name * @param {boolean} flag channel parameter * */ // 加入聊天室 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)); }; /** * Get user from chat channel. * * @param {Object} opts parameters for request * @param {String} name channel name * @param {boolean} flag channel parameter * @return {Array} users uids in channel * */ // 从聊天室中获取用户 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; }; /** * Kick user out chat channel. * * @param {String} uid unique id for user * @param {String} sid server id * @param {String} name channel name * */ // 将用户移除聊天室 ChatRemote.prototype.kick = function(uid, sid, name, cb) { 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); cb(); };
// -------------------------------------------------------------------------------- // chatHandler.js // -------------------------------------------------------------------------------- var chatRemote = require('../remote/chatRemote'); module.exports = function(app) { return new Handler(app); }; var Handler = function(app) { this.app = app; }; var handler = Handler.prototype; /** * Send messages to users * * @param {Object} msg message from client * @param {Object} session * @param {Function} next next stemp callback * */ handler.send = function(msg, session, next) { // 客户端向服务端发起聊天请求,请求消息包括聊天内容,发送者和发送目标信息。 // 消息的接收者可以聊天室里所有的用户,也可以是某一特定用户。 var rid = session.get('rid'); 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); // 如果发送目标是所有用户,服务器端首先会选择channel中的所有用户, // 然后向channel发送消息,最后前端服务器就会将消息分别发送给channel中取到的用户 //the target is all users if (msg.target == '*') { channel.pushMessage('onChat', param); } // 如果发送目标只是某一特定用户,发送过程和之前完全一样, // 只是服务器端首先从channel中选择的只是一个用户,而不是所有用户。 //the target is specific user else { var tuid = msg.target + '*' + rid; var tsid = channel.getMember(tuid)['sid']; channelService.pushMessageByUids('onChat', param, [{ uid: tuid, sid: tsid }]); } next(null, { route: msg.route }); };
原文地址:http://blog.csdn.net/xufeng0991/article/details/45029171