![](/img/trans.png)
[英]How to prevent multiple event listeners being added with jquery and socket.io
[英]How To Prevent Multiple Socket.io Event Listeners
我需要能够从我的 socket.io 事件侦听器访问 req。 所以我这样做了:
服务器
var express = require('express'),
app = express(),
app.set('view engine', 'pug');
app.use(express.static(__dirname + '/public'));
client = require('socket.io').listen(8080).sockets;
app.get('/', function (req, res) {
client.on('connection', function (socket) {
console.log("Connection")
});
res.render('chat');
});
app.listen(config.server.port, function() {
console.log("Listening on port " + config.server.port);
});
客户:
try {
var socket = io.connect('http://127.0.0.1:8080');
} catch(e) {
//Set status to warn user
console.log(e);
}
问题是,如果您在快速路由处理程序中添加 socket.io 事件侦听器,则会在套接字上创建多个侦听器。 如果您要创建 pug 文件并测试此代码,您会注意到控制台在第一次刷新时记录“连接”一次,然后在第二次刷新两次,依此类推,因为每次处理路由时都会添加另一个事件侦听器。 我可以通过将侦听器移到路由处理程序之外来修复它,但是我需要能够访问“req”。 是否有任何解决方案可以让我访问“req”并防止添加过多的侦听器?
不幸的是, req
在socket.io访问的对象connection
事件侦听器是req
当事件监听器被声明,而不是req
执行事件侦听器时。 因此,问题中提到的预期行为(如果我的理解是正确的)是不可能的。
这是一个简单的实验,对于有问题的代码:
app.get('/', function (req, res) {
client.on('connection', function (socket) {
console.log("req.url: " + req.url)
});
res.render('chat');
});
如果使用浏览器发送 2 个 HTTP 请求:首先GET /?q=42
,然后GET /?q=88
,则console.log
结果将是:
//first request
req.url: /?q=42
//second request
req.url: /?q=42
req.url: /?q=88
对于第二个请求,由于connection
事件被监听两次,事件监听器也将被执行两次。 但是,执行结果不同——第一个 HTTP 请求中附加的事件侦听器记住了当时的req
对象值。
如果只有一个客户端,并且没有并发请求(一种非常受限的情况),则有一种解决方法——将req
保存为currentReq
,并让事件侦听器处理currentReq
:
var currentReq;
var isListened = false;
// write logic in middleware, so it can be used in all routes.
app.use(function(req, res, next) {
currentReq = req;
if (!isListened) {
client.on('connection', function (socket) {
console.log("req.url: " + currentReq.url)
});
isListened = true;
}
next();
});
app.get('/', function (req, res) {
res.render('chat');
});
这里有一些思考为什么这是不可能的。
问题中的场景是:
GET
请求 很明显,当connection
事件发生时(第 5 步),HTTP req
(第 1 步)早已消失。 由于WebSocket 和HTTP 是不同的连接,在不同的端口上,因此无法恢复步骤5 中的初始HTTP 请求信息。
您可以通过控制台记录io.sockets
来查看记录的事件侦听io.sockets
Namespace {
_events:
{ connection: [ [Function], [Function], [Function], [Function] ] },
_eventsCount: 1 }
要解决您的问题,只需在初始化套接字连接之前放置此条件
if(io.sockets._events == undefined) {
io.on('connection', socket => {
...
});
}
如果除了connection
之外还有其他事件侦听connection
if(!('connection' in io.sockets._events)) {
io.on('connection', socket => {
...
});
}
聚会有点晚了,但似乎 OP 想要访问 req 对象来验证登录。 要将我的 2 便士添加到组合中,这就是我在类似情况下所做的:
随着我的应用程序使用基于 cookie 的登录令牌(仅通过 https!),我能够使用
function cookieParser(cookief) {
let cookies = {}
let _cookies = cookief.split("; ")
for(cookiekv of cookief.split("; ")) {
let kv = cookiekv.split("=")
cookies[kv[0]] = kv[1]
}
return cookies
}
function verifyLogin(cookies) {
//verify login cookies here. Change this example
return cookies.loginToken
}
io.use(function(socket,next) {
const cookies = cookieParser(socket.handshake.headers.cookie)
if(verifyLogin(cookies)) {
next()
} else {
next(new Error("invalid login token"))
}
})
这允许我只接受来自已登录并收到登录令牌的用户的套接字连接。 这也应该解决您的多个侦听器场景,因为在io
上只注册了一个侦听器。
希望这会有所帮助!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.