Socket.io 学习笔记三(转载)
前言
上次说到关于socket连接授权的问题,有好心人提示说到session-socket开源项目,可以在建立连接后进行session相关操作, 操作挺方便,很感谢这位朋友。我的目的是在握手阶段,通过会话判定权限,暨是否建立连接,所以只能自己查阅中间件代码,然后COPY需要的。
Connect Session
Connect相关的会话存储我知道四种,cookie-based,memory-store,redis,mongodb.
cookie-based方案数据直接存储在cookie里面,解析之后即可获得。
memory-store方案,使用内存做容器,来存储数据,由于中间件使用了闭包特性,如果不对中间件动手脚的话,是无法访问这些数据,而且我是Express和socket.io分离的,更不可能拿到数据,所以此路不通。
connect-redis方案,将数据放入redis数据库,这样不同进程都可以获取到session数据。Cookie解析之后获取sessionId,通过sessionId从redis获取数据。
mongodb方案,跟redis机理相同,不再赘述。
Connect-redis
- 启用connect-redis session。
var express =require('express'); var app = express(); var RedisStore = require('connect-redis')(express); app.use(express.cookieParser('love')); app.use(express.session({key: 'lovestory', secret: 'lovestory', store: new RedisStore({ host: '127.0.0.1', port: 6379}) }));
- socket.io部分
var io = require('socket.io').listen(1338); var utils = require('connect/lib/utils.js'); var cookie = require('connect/node_modules/cookie-parser/node_modules/cookie'); var redis = require('redis'); var client = redis.createClient(6379,'127.0.0.1'); //连接授权判定,相关参数需要跟启用Session的参数相同 io.sockets.authorization(function (handshakeData, callback) { //cookie解析,获取sessionId var cookies = handshakeData.headers.cookie; var secret = 'lovestory'; var key = 'lovestory'; var prefix = 'sess:'; var sessionId = null; if (cookies) { cookies = cookie.parse(cookies); cookies = utils.parseSignedCookies(cookies, secret); cookies = utils.parseJSONCookies(cookies); if(cookies[key]) { sessionId = cookies[key]; } } //从redis获取数据 var sid = prefix + sessionId; client.get(sid,function(err,data){ var result = JSON.parse(data); if (result.user){ for (var i in handshakeData) { delete handshakeData[i]; } handshakeData.user = result.user; callback(null,true); }else{ callback(null,false); } }); }); //存储user,socketId键值对,需要单点推送的时候通过user获取对应的socketId即可。 io.sockets.on('connection', function (socket) { client.set(socket.handshake.user,socket.id); io.sockets.socket(socket.id).send('Do you love this girl!!!'); });
Cookie-based更加简单一些
- Express部分
var express =require('express'); var app = express(); app.use(express.cookieParser('lovestory')); app.use(express.cookieSession({key: 'lovestory', secret : 'lovestory'});
- socket.io部分
var io = require('socket.io').listen(1338); var utils = require('connect/lib/utils.js'); var cookie = require('connect/node_modules/cookie-parser/node_modules/cookie'); //连接授权判定,相关参数需要跟启用Session的参数相同 io.sockets.authorization(function (handshakeData, callback) { //cookie解析,获取cookie中的session数据 var cookies = handshakeData.headers.cookie; var secret = 'lovestory'; var key = 'lovestory'; if (cookies) { cookies = cookie.parse(cookies); cookies = utils.parseSignedCookies(cookies, secret); cookies = utils.parseJSONCookies(cookies); if(cookies[key]) { session = JSON.parse(cookies[key]); } } });
问题引申
上述代码比较原始,因为基本上是COPY出来的,不过可以实现我最初的想法。但是新问题也冒出来了,我如何得知,什么时候,需要向哪些用户,推送什 么消息。例如SNS,一位用户更新了状态,需要推送给他的朋友,就是需要主动向浏览器端推送消息的时候,如何通知到这个进程。。。。。。。
粗略构想(“进程”只为叙述方便,略有不妥)
Websocket握手阶段是通过HTTP协议完成的,socket.io负责监听upgrade,connection事件,可以与普通 HTTP server共存。通过HTTP模块监听普通http请求,当业务逻辑进程完成相应工作,判定需要向其他用户推送消息时,向推送进程发出http请求,附 带需推送内容即可。
业务逻辑进程与推送进程建立socket连接,当业务逻辑进程完成相应工作,判定需要向其他用户推送消息时,通过socket发送消息即可。
简易实现
- socket.io与普通HTTP服务同时存在
var http = require('http'); var app = http.createServer(function(req,res){}); var io = require('socket.io').listen(app); app.listen(1338);
HTTP请求body部分目测需要包含以下信息。
type
必需。 single
,room
,或all
,对应单点推送,群组推送,全局推送pointer
必需。指明需要推送的目标,若 type
为all
,显式声明truecontent
必需。指明需要推送的内容 事件监听函数(请原谅我的大嵌套~~|||)
function(req,res){ jsonBody(req,res,function(err,data){ if(err) { res.statusCode = 400; res.end('Bad request, json body only'); }else{ if (data.type && data.pointer && data.content) { switch (data.type) { case 'all' : io.sockets.send(data.content); res.end('success'); break; case 'room' : io.sockets.in(data.pointer).send(data.content); res.end('success'); break; case 'single' : client.get(data.pointer, function(err,result){ if(result) { io.sockets.socket(result).send(data.content); res.end('success'); } }) default : res.end('unexpected type'); break; } }else{ res.end('Bad request!'); } } }) }
代码还欠缺很多打磨,不过基本功能已经出来了。
- 关于服务器间socket连接,基本理念是相同的,就不再往坑里跳。
个人总结
到此为止,socket.io常规使用已经全部over,涉及到的相关代码私下继续打磨。