Use socket.io inside a express routes file

后端 未结 6 1071
刺人心
刺人心 2020-11-28 05:05

I\'m trying to use Socket.io with Node.js and emit to a socket within the logic of a route.

I have a fairly standard Express 3 setup with a server.js file that sits

相关标签:
6条回答
  • 2020-11-28 05:41

    aarosil's answer was great, but I ran into the same problem as Victor with managing client connections using this approach. For every reload on the client, you'd get as many duplicate messages on the server (2nd reload = 2 duplicates, 3rd = 3 duplicates, etc).

    Expanding on aarosil's answer, I used this approach to use the socket object in my routes file, and manage the connections/control duplicate messages:

    Inside Server File

    // 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);
    

    Inside routes file

    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');
    
          })
    
       });
    }
    
    0 讨论(0)
  • 2020-11-28 05:41

    Super late addition here, but I wanted to access a socket in my routes and specifically wanted to broadcast a message after saving to the database. I used the answer provided by @aarosil to set/get the io object, sent the each client its socket id on connection, then used the socket id in the route to be able to use socket.broadcast.emit() instead of io.emit().

    In server:

    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
    })
    

    I send the socket id with each req and then I can do the following in my routes:

    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)
    })
    
    
    0 讨论(0)
  • 2020-11-28 05:42

    There is a better way to do this now with Express 4.0.

    You can use app.set() to store a reference to the io object.

    Base configuration:

    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);
    

    Inside route or middleware:

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

    Information about app.set() and app.get() is below:

    app.set(name, value)

    Assigns setting name to value. You may store any value that you want, but certain names can be used to configure the behavior of the server. These special names are listed in the app settings table.

    Calling app.set('foo', true) for a Boolean property is the same as calling app.enable('foo'). Similarly, calling app.set('foo', false) for a Boolean property is the same as calling app.disable('foo').

    Retrieve the value of a setting with app.get().

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

    0 讨论(0)
  • 2020-11-28 05:50

    Whats wrong with just using

    global.io = require('socket.io').listen(server);
    
    0 讨论(0)
  • 2020-11-28 05:56

    You can set up your routes file as a function, and pass the Socket.IO object when requiring the file.

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

    Then require routes like this:

    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);
    
    0 讨论(0)
  • 2020-11-28 05:57

    module.parent.exports.server would also work if you exported server in the parent module.

    0 讨论(0)
提交回复
热议问题