Maintaining References to Sockets with Express and Socket.io

I hit a frustrating problem when trying to use expressjs alongside socket.io recently. The solution may seem somewhat trivial, but I struggled with it for a while, until finally asking for help on the socket.io irc channel. I’m not sure if a better solution exists for my case, but this is what I have for now.

In the main route of my express app, I needed to iterate across an array, performing a REST call using each element as a parameter and spit the results back for socket.io to send out to the browser. To get some idea of what that would look like, imagine the following.

app.get('/', function(req, res){
for(var i=0; i < someArray.length; i++){
 var someVariable = doSomething(function(err, results){
   socket.emit('msg', results);
});
}
});

My problem then became, how do I emit in this way, given my use case? All examples I could find on the socket.io homepages demonstrate emitting msgs on connection, like this:

io.sockets.on('connection', function (socket) {
  // send custom events to browser socket
  socket.emit('hi', { 'i can send': 'json!' });
});

But I wanted to emit messages as part of a loop, within an app route. How would I do that? I could wrap the loop with the connection logic:

app.get('/', function(req, res){
io.sockets.on('connection', function (socket) {
for(var i=0; i < someArray.length; i++){
var someVariable = doSomething(function(err, results){
   socket.emit('msg', results);
});
}
});
});

But if I do this, then my connection listener would be added on every refresh of the route. This would mean I'd end up with duplicate binding of event listeners and a load more results messages would be emitted than I'd expect. This would result in crazy behavior for any user who might choose to refresh your app.

Actually, the solution is rather simple although not entirely obvious given the nature of using node. If you maintain a reference to the socket as a global variable and then later emit upon that same socket, like so:

var global_socket;

io.sockets.on('connection', function (socket) {
  global_socket = socket;
});

app.get('/', function(req, res){
for(var i=0; i < someArray.length; i++){
 var someVariable = doSomething(function(err, results){
   global_socket.emit('msg', results);
});
}
});

This way, a connection will be established and maintained and we won't get duplicate bindings when refreshing the route of the app. Simple when you know how. I had a rather interesting discussion following this on irc about whether the use of express and socket.io together is warranted, given that socket.io supports use of variable passing and ultimately could entirely replace an express app.

8 comments so far

  1. Bosky

    hi, wont this allow only one client to connect.

    i’ve passed some id from client to server, subsequently made it an array.

    //for this session, see if there is a socket available
    app.get(/hash/:hash’, function(req,res){
    ..
      //calculate _hash or format it
      if(global_sockets[_hash]) {
        //emit or do something
       }
    ..
    });
     
    //and when the client first contacts the server
    var global_sockets = {};
    io.sockets.on('connection', function(socket){
       ..
       socket.on(‘c2s’, function(session){
           //calculate hash from session
         global_sockets[_hash] = socket;
         console.log(‘now try localhost/hash/, _hash);
         .. 
      }); 
      ..
    });

    i’m building up something on these lines. i need to figure out resetting global_sockets[_hash] when a disconnect happens though.

    ~B

  2. Ian

    Indeed it does! The most recent connection will boot off any other simultaneous ones.

    Maintaining an array in the way you show is a good way to go.

  3. Bosky, keep a reference to the socket id (which is the only unique thing per connection), do something like this:

    socket_io.on(‘connection’, function (socket) {
    global_sockets[socket.id] = socket;
    // on disconnect do:
    .on(‘disconnect’, function () {
    delete global_sockets[socket.id];
    })
    });

  4. Toby1

    Oh man thank you so much for posting this.
    I have been searching for a solution to this all over.
    So simple. Brilliant.

  5. Andy

    Thanks for your post! Very helpful.

  6. Andy

    Hello Ian,

    Going from what Bosky and alessio have written, I think what you want to do is something like what I’ve described below. When a new connection is established, add that socket connection to a list of sockets, which I’ve named clients:

    io.sockets.on(‘connection’, function(client) {

    // Adds socket to list of sockets
    clients.push(client);

    // Removes socket from list of sockets
    client.on(‘disconnect’, function() {
    clients.splice(clients.indexOf(client), 1);
    });
    });

    Then later, when you want to broadcast over those sockets with some sort of message, then iterate over that list of sockets like so:


    app.get('/dosomething', function (req, res) {

    console.log('there are currently ' + clients.length + ' connections.');

    for(var i = 0; i < clients.length; i++) {
    (clients[i]).emit('msg','yo');
    }
    res.send('Hi'); // msg sent to
    });

    In that example, I am using /dosomething as a trigger to send a message over all the connections.

    I’m not sure this is the preferred way to do such a thing, but it works for now. Maybe there’s a better way, but since I have about 1 week of socket.io experience, this way is what I’m going with. :)

    Andy

  7. Brandon Lockaby

    If you want to get a reference to each presently connected socket, use

    var clients = io.sockets.clients();

    -or-

    var clients = io.sockets.clients('room');
  8. thanks for share that’s is i’m looking for..:)

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>