Node.js 中socket.io聊天室的问题
Node.js 中socket.io聊天室的问题
在跟随书上教程搭建socket.io的时候我遇到了以下问题:
io.sockets.on('connection',function(socket){
console.log('init');
socket.on('getAllMessages',function(){
socket.emit('allMessages',messages);
})
socket.on('createMessage',function(message){
messages.push(message);
io.sockets.emit('messageAdded',message);
})
})
io.set('authorization',function(handshakeData,accept){
handshakeData.cookie = Cookie.parse(handshakeData.headers.cookie);
var connectSid = handshakeData.cookie['connect.sid'];
if(connectSid){
connectSid = parseSignedCookie(connectSid,'technode');
sessionStore.get(connectSid,function(error,session){
if(error){
accept(error.message,false);
}else{
handshakeData.session = session;
if(session._userId){
accept(null,true);
}else{
accept('No login');
}
}
})
}else{
accept('No session');
}
})
这段代码中,如果我加入了io.set(‘authorization’,)的代码段,io.sockets.on(‘connection’)里面的代码就得不到执行。。请问是什么原因?
在使用 socket.io
搭建聊天室时,你遇到的问题可能是由于 io.set('authorization')
的异步特性导致的。当 authorization
函数执行时,它会异步地检查用户的会话,而在这个过程中,io.sockets.on('connection')
事件处理函数可能已经被执行了。
原因分析
当你在 socket.io
服务器初始化时设置了 authorization
钩子,socket.io
会在每个连接到来时调用这个钩子进行权限验证。如果钩子中的异步操作没有完成,socket.io
无法确定连接是否应该被接受,因此可能导致后续的连接处理逻辑(如 io.sockets.on('connection')
)不能正常执行。
解决方案
为了确保 authorization
钩子中的异步操作完成后,再执行连接处理逻辑,你需要正确处理异步回调。可以使用 callback
参数来通知 socket.io
验证结果。
示例代码
const io = require('socket.io')(server);
const cookie = require('cookie');
const connect = require('connect');
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
// 创建一个 Redis 存储实例
const sessionStore = new RedisStore({
// Redis 连接配置
});
io.set('authorization', function (handshakeData, accept) {
const cookies = cookie.parse(handshakeData.headers.cookie || '');
const connectSid = cookies['connect.sid'];
if (!connectSid) {
return accept('No session', false);
}
sessionStore.get(connectSid.replace(/(^.+; )?connect\.sid=|;.*$/g, ''), function (error, session) {
if (error) {
return accept(error.message, false);
}
if (!session || !session._userId) {
return accept('No login', false);
}
handshakeData.session = session;
return accept(null, true);
});
});
io.sockets.on('connection', function (socket) {
console.log('A user connected');
socket.on('getAllMessages', function () {
socket.emit('allMessages', messages);
});
socket.on('createMessage', function (message) {
messages.push(message);
io.sockets.emit('messageAdded', message);
});
});
解释
- cookie 解析:我们使用
cookie
库解析客户端发送的 cookie。 - session 获取:通过
sessionStore.get
方法从 Redis 中获取会话数据。 - 错误处理:如果在获取会话数据时发生错误,或者会话不存在或用户未登录,直接返回错误信息并拒绝连接。
- 会话存储:将会话数据存储到
handshakeData
对象中,以便后续的连接处理逻辑可以访问这些数据。
通过这种方式,你可以确保 authorization
钩子中的异步操作完成后再执行 connection
事件处理逻辑,从而避免你的问题。
你的代码要求从客户端浏览器中发送connect.sid这个cookie 名称。你说无法执行io.sockets.on(‘connection’)这个,估计就是这个原因。你可以在客户端脚本中加入error事件, 应该很快就知道这个error事件中的参数的值是 No session 或 No login
你把io.set('authorization’)整个方法注释掉 的含义是指不启用认证。 你把io.set('authorization’)内的代码全部注释掉,这个是启用了认证,但没有明确表示认证通过,所以就无法连接了. 你可以加上io.set('authorization’)这个,然后这个事件中直接加上accept(null,true);应该就可以了。 另外,你使用的不是1.0以上的版本吧。。
你在代码中使用了io.set('authorization', ...)
来实现基于会话的身份验证。然而,在authorization
函数中的异步操作(即获取会话)可能没有正确完成,导致accept
函数没有被调用。如果accept
没有被调用,那么客户端的连接就不会被接受,因此io.sockets.on('connection')
中的回调也不会被执行。
为了解决这个问题,你需要确保在accept
中处理所有情况,并且始终调用它。你可以通过添加错误处理来确保accept
总是被调用,即使发生了错误。以下是修改后的示例:
io.set('authorization', function(handshakeData, accept) {
handshakeData.cookie = Cookie.parse(handshakeData.headers.cookie);
var connectSid = handshakeData.cookie['connect.sid'];
if (connectSid) {
connectSid = parseSignedCookie(connectSid, 'technode');
sessionStore.get(connectSid, function(error, session) {
if (error) {
return accept(error.message, false); // 确保错误时调用accept
} else {
handshakeData.session = session;
if (session._userId) {
return accept(null, true); // 确保成功时调用accept
} else {
return accept('No login', false); // 确保未登录时调用accept
}
}
});
} else {
return accept('No session', false); // 确保无会话时调用accept
}
});
这样可以确保无论成功、失败还是其他情况,accept
都被调用。这将允许客户端连接并继续执行io.sockets.on('connection')
中的代码。
如果你使用的是较新的socket.io
版本,set
方法已被弃用,你应该使用use
方法来设置中间件。下面是使用use
方法的示例:
io.use(function(socket, next) {
var handshakeData = socket.request;
handshakeData.cookie = Cookie.parse(handshakeData.headers.cookie);
var connectSid = handshakeData.cookie['connect.sid'];
if (connectSid) {
connectSid = parseSignedCookie(connectSid, 'technode');
sessionStore.get(connectSid, function(error, session) {
if (error) {
return next(new Error(error.message)); // 使用next传递错误
} else {
handshakeData.session = session;
if (session._userId) {
return next(); // 没有错误,继续下一个中间件
} else {
return next(new Error('No login')); // 未登录时传递错误
}
}
});
} else {
return next(new Error('No session')); // 无会话时传递错误
}
});
通过这种方式,你可以确保身份验证逻辑正确处理,并允许正常的连接流程继续进行。