繁体   English   中英

如何防止多个 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');
});

这里有一些思考为什么这是不可能的。

问题中的场景是:

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

很明显,当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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM