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.
hi, wont this allow only one client to connect.
i’ve passed some id from client to server, subsequently made it an array.
i’m building up something on these lines. i need to figure out resetting global_sockets[_hash] when a disconnect happens though.
~B
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.
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];
})
});
Oh man thank you so much for posting this.
I have been searching for a solution to this all over.
So simple. Brilliant.
Thanks for your post! Very helpful.
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
If you want to get a reference to each presently connected socket, use
-or-
thanks for share that’s is i’m looking for..:)