【问题标题】:Use socket.io inside a express routes file在快速路由文件中使用 socket.io
【发布时间】:2013-09-22 06:22:57
【问题描述】:

我正在尝试将 Socket.io 与 Node.js 一起使用,并在路由逻辑内发送到套接字。

我有一个相当标准的 Express 3 设置,其中包含一个位于路由中的 server.js 文件,然后我有一个位于路由文件夹中的 index.js,该文件夹导出站点的所有页面/可公开访问的功能。所以它们看起来像:

exports.index = function (req, res) {
    res.render('index', {
        title: "Awesome page"
    });
}; 

使用 server.js 中定义的路由,例如:

app.get('/',routes.index);

我假设我必须在 server.js 中创建 socket.io 对象,因为它需要服务器对象,但我如何访问该对象并从 index.js 导出函数向它发出?

【问题讨论】:

    标签: node.js sockets socket.io


    【解决方案1】:

    现在有了 Express 4.0 的更好方法。

    您可以使用app.set() 来存储对io 对象的引用。

    基本配置:

    var app = require('express')();
    var server = app.listen(process.env.PORT || 3000);
    var io = require('socket.io')(server);
    // next line is the money
    app.set('socketio', io);
    

    内部路由或中间件:

    exports.foo = function(req,res){
        // now use socket.io in your routes file
        var io = req.app.get('socketio');
        io.emit('hi!');
    }
    

    关于app.set()app.get()的信息如下:

    app.set(name, value)

    将设置名称分配给值。你可以存储任何你想要的值, 但某些名称可用于配置服务器的行为。 这些特殊名称在app settings table 中列出。

    为布尔属性调用 app.set('foo', true) 与 打电话给app.enable('foo')。同样,调用app.set('foo', false) 布尔属性与调用 app.disable('foo') 相同。

    使用app.get() 检索设置的值。

    来源:https://expressjs.com/en/api.html#app.set

    【讨论】:

    • 我真的很喜欢关于使用 app.set() 来存储这样的引用的第二个意见。如果它坚固,那将是一种很好的方法。
    • 我当时从 socket.io API 文档中获取了模式,现在检查一下可能是不同的方式
    • 是的,我不认为他们在文档中提到它是任何选择。但它看起来超级光滑。
    • 有人已经用过这个方法了?我有问题。当页面重新加载或套接字重新连接时,所有发射均由 socked 两次、三次等...没有解决方法(我也找不到)。使用快速路由的 socketio 似乎是不可能的
    • @marcvander 你不能在一个端点内监听你只能监听 app.js 或 server.js 但你可以在路由文件中发出
    【解决方案2】:

    您可以将您的路由文件设置为一个函数,并在需要该文件时传递 Socket.IO 对象。

    module.exports = function(io) {
      var routes = {};
      routes.index = function (req, res) {
        io.sockets.emit('payload');
        res.render('index', {
          title: "Awesome page"
        });
      };
      return routes;
    };
    

    然后需要这样的路由:

    var express = require('express');
    var app = express();
    var http = require('http');
    var server = http.createServer(app);
    var io = require('socket.io').listen(server);
    var routes = require('./routes')(io);
    

    【讨论】:

    • 好吧,这确实需要一些重构。我希望有一种更清洁的方式
    • 这个解决方案很好,但如果你想看到一种更简洁直观的类似方法,请查看我在 Logan Tegman stackoverflow.com/questions/29872317/…找到的这个答案
    • 你的方法利用了依赖注入,我喜欢它而不是全局状态变量解决方案。
    【解决方案3】:

    aarosil 的回答很棒,但我遇到了与 Victor 相同的问题,即使用这种方法管理客户端连接。对于客户端上的每次重新加载,您会在服务器上获得尽可能多的重复消息(第 2 次重新加载 = 2 个重复,第 3 次 = 3 个重复,等等)。

    扩展 aarosil 的答案,我使用这种方法在我的路由文件中使用套接字对象,并管理连接/控制重复消息:

    内部服务器文件

    // same as aarosil (LIFESAVER)
    const app = require('express')();
    const server = app.listen(process.env.PORT || 3000);
    const io = require('socket.io')(server);
    // next line is the money
    app.set('socketio', io);
    

    内部路由文件

    exports.foo = (req,res) => {
    
       let socket_id = [];
       const io = req.app.get('socketio');
    
       io.on('connection', socket => {
          socket_id.push(socket.id);
          if (socket_id[0] === socket.id) {
            // remove the connection listener for any subsequent 
            // connections with the same ID
            io.removeAllListeners('connection'); 
          }
    
          socket.on('hello message', msg => {
            console.log('just got: ', msg);
            socket.emit('chat message', 'hi from server');
    
          })
    
       });
    }
    

    【讨论】:

    • 这是一个很好的答案。像魅力一样工作
    【解决方案4】:

    这里添加的超级晚,但我想在我的路由中访问一个套接字,并且特别想在保存到数据库后广播一条消息。我使用@aarosil 提供的答案来设置/获取 io 对象,在连接时向每个客户端发送其套接字 ID,然后在路由中使用套接字 ID 以便能够使用 socket.broadcast.emit() 而不是 io.emit()

    在服务器中:

    const io = require('socket.io')(server)
    app.set('socketio', io)
    
    io.on('connect', socket => {
      socket.emit('id', socket.id) // send each client their socket id
    })
    

    我用每个请求发送套接字 id,然后我可以在我的路由中执行以下操作:

    router.post('/messages', requireToken, (req, res, next) => {
    
      // grab the id from the request
      const socketId = req.body.message.socketId
    
      // get the io object ref
      const io = req.app.get('socketio') 
    
      // create a ref to the client socket
      const senderSocket = io.sockets.connected[socketId]
    
      Message.create(req.body.message)
        .then(message => {
    
          // in case the client was disconnected after the request was sent
          // and there's no longer a socket with that id
          if (senderSocket) {
    
            // use broadcast.emit to message everyone except the original
            // sender of the request !!! 
            senderSocket.broadcast.emit('message broadcast', { message })
          }
          res.status(201).json({ message: message.toObject() })
        })
        .catch(next)
    })
    
    

    【讨论】:

      【解决方案5】:

      只使用有什么问题

      global.io = require('socket.io').listen(server);
      

      【讨论】:

      • 有效。感觉太 hacky.. 但这可能是最简单的解决方案,特别是如果您需要在处理请求或中间件之外访问 io 对象。
      • 这是一个出色的解决方案,值得更多称赞以易于使用。在使用 express-generator 并将逻辑分布在多个文件中时尤其有效。
      【解决方案6】:

      在服务器文件中,

          const app = express();
          const server = require("http").createServer(app);
          
          const io = require("socket.io")(server);
          io.on("connection", (socket) => {
            app.set("socket", socket);
          });
      

      在任何控制器中,

           const socket = req.app.get("socket");
           socket.emit(customerId, { test: "something" });
      

      【讨论】:

        【解决方案7】:

        如果您在父模块中导出服务器,module.parent.exports.server 也可以工作。

        【讨论】:

          猜你喜欢
          • 2019-10-17
          • 2014-10-31
          • 2018-10-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-27
          • 1970-01-01
          相关资源
          最近更新 更多