Created
March 28, 2016 09:46
-
-
Save stableShip/be355b3b58b70f442dcf to your computer and use it in GitHub Desktop.
聊天模块连接管理
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 聊天模块通讯管理 | |
| ---------- | |
| ## socket现有管理方案 | |
| ### 连接socket管理 | |
| ###### `(1. socket.io的封装,目前在/model/socketio/,socket.io服务是承载在默认3001端口(配置在/model/config/index)的http server上,socket事件的监听模式是怎样?)` | |
| 在socketio服务端启动时创建了一个`conns: {},` key为用户的uid,value为用户连接socketio的socket对象引用. | |
| 当用户连接时将用户uid和socket关联起来. | |
| 具体实现: | |
| 服务端socket.io服务器启动时,在model/socketio/service/index.js 监听connection事件,并注册`message, disconnect, error`事件监听. | |
| 在model/index.js中注册了`C2S_REGISTER_REQ: 注册身份请求`的onMessage处理方法,并监听了`close`事件 | |
| 当客户端登陆时,发送`C2S_REGISTER_REQ: 注册身份请求`请求给服务端 | |
| ``` | |
| // 客户端发送socketio消息 | |
| message_sign.send({ | |
| type: MsgType.C2S_REGISTER_REQ, | |
| data:{ | |
| type:'user', | |
| uid:data.uid, | |
| deviceid:data.deviceid | |
| } | |
| }); | |
| ``` | |
| ()触发服务端`message`事件,监听方法执行: | |
| ``` | |
| client.on('message', function (str) { | |
| try{ | |
| var json; | |
| if(typeof str == 'string'){ | |
| json = JSON.parse(str); | |
| } | |
| else { | |
| json = str; | |
| } | |
| o.emit('data', this, json); | |
| o.onMessage(this, json); // 调用message方法,根据type进行通讯分发处理 | |
| }catch (e){ | |
| o.emit('data', this, str); | |
| } | |
| }); | |
| ``` | |
| 调用message方法,根据type进行通讯分发处理 | |
| ``` | |
| // Dispatch a client message | |
| onMessage: function(conn, msg){ | |
| console.log("onMessage:",msg); | |
| if(!msg.type || !this.types[msg.type]) return; | |
| if(this.types[msg.type].onMessage){ | |
| this.types[msg.type].onMessage(conn, msg); | |
| } | |
| } | |
| ``` | |
| 调用`C2S_REGISTER_REQ: 注册身份请求`类型的onMessage方法,此时会执行: | |
| ###### `2. 客户端socket连接的管理是在/model/index中,关键是消息:C2S_REGISTER_REQ的处理逻辑,这里面对socket connection是怎么管理的?请按不同的业务场景描述管理流程;` | |
| ``` | |
| model.jmsocket.register_type(MsgType.C2S_REGISTER_REQ,{ | |
| onMessage:function(conn,msg){ | |
| //省略 | |
| var curconn = model.jmsocket.conns[conn.uid]; | |
| //检测用户是否 异地登陆,通知客户端进行异地登陆处理 | |
| if(curconn && conn != curconn){ | |
| model.jmsocket.sendTo(conn.uid,{ | |
| type: MsgType.S2C_DIFFERENT_CONNECTION, | |
| data: {} | |
| }); | |
| } | |
| // 绑定用户uid和socket映射 | |
| model.jmsocket.conns[conn.uid] = conn; | |
| //省略 | |
| } | |
| ``` | |
| 当用户退出时,请求`/signout`路由,,服务端进行socket处理: | |
| ``` | |
| // 注销后,服务端删除uid与socket之间的关系 | |
| var conn = model.jmsocket.conns[id]; | |
| if (conn) { | |
| conn.disconnect(true); // 断开所有连接 | |
| delete model.jmsocket.conns[id]; //删除映射关系 | |
| } | |
| ``` | |
| 为了保证删除,在model/index.js中监听了socket的close事件,在socket关闭时,删除映射 | |
| ``` | |
| if(onconn && typeof(onconn.id) != 'undefined') { | |
| //省略 | |
| delete model.jmsocket.conns[conn.uid]; | |
| //省略 | |
| } | |
| ``` | |
| ## 通讯协议 | |
| 定义了19种通讯类型: | |
| ``` | |
| { C2S_REGISTER_REQ : 1, | |
| S2C_REGISTER_RES : 2, | |
| S2C_ALONECHAT_PUSH : 3, | |
| S2C_SIGNED_PUSH : 4, | |
| S2C_SYSTEM_PUSH : 5, | |
| S2C_RESERVATION_PUSH : 6, | |
| S2C_DOCTORMSG_PUSH : 7, | |
| S2C_TEAMMATECHAT_PUSH : 8, | |
| S2C_DOCTORFRIENDCHAT_PUSH : 9, | |
| S2C_FRIENDAPPLY_PUSH : 10, | |
| S2C_SOCIAL_PUSH : 11, | |
| S2C_DIFFERENT_CONNECTION : 12, | |
| S2C_REMIND_PUSH : 13, | |
| S2C_UNSIGNED_PUSH : 14, | |
| S2C_TEAMCHAT_PUSH : 15, | |
| S2C_TEAMMEMBER_ENTER : 16, | |
| S2C_TEAMMEMBER_LEAVE : 17, | |
| S2C_VIDEOCALL_INVITE:18, | |
| S2C_VIDEOCALL_END:19 | |
| } | |
| ``` | |
| 所有的通讯都通过通讯类型进行区分处理,即使是讨论组聊天,也是遍历所有用户,进行一个个的通讯, | |
| 降低了系统复杂度. | |
| 因为我们系统不是所有用户实时在线,也只能通过这种形式进行消息广播.不在线用户进行推送通知.同时记录聊天记录. | |
| ## http服务器和socketio服务器之间的通讯 | |
| 在 model/index.js 中创建了一个`o`的内置对象,将该对象传入`createServer`方法,用于保存用户socket连接,在socketio服务器触发message事件时,进行消息处理,可以理解为闭包. | |
| ``` | |
| module.exports = function(opts) { | |
| if(!opts.port) return; | |
| var o = { | |
| // Client message types | |
| types: {}, | |
| // Client connections (bind with: key=uid, value=socket.io.client) | |
| conns: {}, | |
| // Register an event (message type) handler | |
| register_type: function(type, opts){ | |
| this.types[type] = opts; | |
| }, | |
| // Unregister an event (message type) handler | |
| unregister_type: function(type, opts){ | |
| delete this.types[type]; | |
| }, | |
| // Dispatch a client message | |
| onMessage: function(conn, msg){ | |
| console.log("onMessage:",msg); | |
| if(!msg.type || !this.types[msg.type]) return; | |
| if(this.types[msg.type].onMessage){ | |
| this.types[msg.type].onMessage(conn, msg); | |
| } | |
| } | |
| }; | |
| // Use jm-common/event to handle events | |
| jm.enableEvent(o); | |
| // Constructor | |
| createServer(o, opts); | |
| return o; | |
| } | |
| function createServer(o, opts) { | |
| var s = http.createServer(function (req, res) { | |
| res.writeHead(404); | |
| res.end(); | |
| }).listen(opts.port); | |
| var io = server.listen(s); | |
| io.on('connection', function(client) { | |
| //model.logger.info('[Socket.io] Connected to a new user %s', client.id); | |
| client.sendJson = function(data){ | |
| this.send(JSON.stringify(data)); | |
| }; | |
| client.on('message', function (str) { | |
| try{ | |
| var json; | |
| if(typeof str == 'string'){ | |
| json = JSON.parse(str); | |
| } | |
| else { | |
| json = str; | |
| } | |
| o.emit('data', this, json); | |
| o.onMessage(this, json); | |
| }catch (e){ | |
| o.emit('data', this, str); | |
| } | |
| }); | |
| client.on("disconnect", function (reason) { | |
| o.emit('close', this, reason); | |
| }); | |
| client.on("error", function (err) { | |
| o.emit('error', this, err); | |
| }); | |
| o.emit('connect', client); | |
| }); | |
| return o; | |
| } | |
| ``` | |
| http服务器中的其他模块要进行socketio的消息发送时,调用`o`对象的`conns`获取相应的socket连接,进行消息发送: | |
| ``` | |
| model.jmsocket.sendTo = function(uid,json,cb){ | |
| cb = cb || function(){}; | |
| var conn = model.jmsocket.conns[uid]; //model.jmsocket 为 上述的o对象,根据uid从该对象取出用户socket,进行消息发送 | |
| if(conn){ | |
| conn.sendJson(json); | |
| cb(true); | |
| }else{ | |
| cb(false); | |
| } | |
| }; | |
| ``` | |
| ---- | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment