如何防止多个Socket.io事件监听器

我需要能够从我的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); } 

问题是,如果你在一个express路由处理程序中添加了socket.io事件监听器,那么在这个套接字上将创build多个监听器。 如果你要创build帕格文件并testing这段代码,你会注意到控制台在第一次刷新时会logging“连接”一次,因此每次处理路由时都要添加另一个事件监听器。 我可以通过移动路由处理程序之外的侦听器来修复它,但是我需要能够访问“req”。 有没有任何解决scheme,这将使我能够访问“请求”,并防止多余的听众被添加?

不幸的是,在socket.io connection事件监听器中访问的req对象在声明事件监听器时是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'); }); 

这里有一些想法,为什么这是不可能的。

问题的场景是:

  1. 浏览器向Node.js发送HTTP GET请求
  2. Node.js返回HTML页面到浏览器
  3. 浏览器parsing并呈现HTML页面
  4. 浏览器发送WebSocket连接请求到Node.js
  5. Node.jsbuild立WebSocket连接,并在控制台上打印日志。

很显然,当connection事件发生时(步骤5),HTTP req (步骤1)早已不存在了。 在步骤5中无法恢复初始HTTP请求信息,因为WebSocket和HTTP在不同的端口上是不同的连接。