27

I'm having some problems with socket.io, and I have no idea how to solve it. I've an app with a login system with socket.io to manage user interactions. Also I've an array to manage all active websocket sessions. This array stores a pair session.id => username.

Problems begin when a user opens a new tab with the same page (or a different page that uses the websocket). This cause a duplicate username entry in the array.

{id1, username}
{id2, username}

When socket sends a message to the user, it sends only to the first session id of the array, so only one tab receives the message.

I've tried to change the socket id of the new connection to match the other, but it doesn't work.

socket.id = sessionId of the other websocket;

I think another solution is to send the message to all the open sessions, but I think it's not the best way to do that.

Thanks.

Miguel
  • 1,201
  • 2
  • 13
  • 30

4 Answers4

31

It sounds like you may be referring to the socket id in your question, not the session id. If you have an express session you can use the session id, which will be the same regardless of how many tabs are open as long as they use the same browser and it's not in "private" mode. Take a look at this question for how to enable sessions in socket.io.

socket.io and session?

Update: When you're saving the session id, you'll associate it with the user's nickname and the connected sockets. Then you can iterate through each socket and send the message. Something like the following:

[
    {sessionId: '12345', nickname: 'timmay!', socketIds: [1, 2, 3]},
    {sessionId: '23456', nickname: 'pete', socketIds: [4, 5, 6]}
]

When another connection is established you push the new socket id into the socketIds array, and when a user disconnects you remove the id.

Another option might be to only allow one tab per user per session. You can store a single socketId and when a new socket connects for the same user, you can disconnect the original socket and use the new one instead.

Community
  • 1
  • 1
Timothy Strimple
  • 22,920
  • 6
  • 69
  • 76
  • Thanks. I've now the session id within socket.io (socket.handshake.sessionID), but how I can send a message to a specific sessionid like io.sockets.socket(socketid).emit(...), but with sessionid instead socketid? – Miguel Aug 28 '12 at 20:22
  • Thank you. I've got it. A last question. It's possible to send a message to an array of socket ids without a loop of io.sockets.socket(socketid).emit(...)? – Miguel Aug 28 '12 at 22:29
  • Yes and no. There has to be a loop, but you don't necessarily have to write it. You can use an each function like the one provided by underscore. http://underscorejs.org/#each – Timothy Strimple Aug 29 '12 at 00:25
  • @Timothy Strimple what websocket server are you using? – java seeker Apr 03 '14 at 03:23
  • Another solution might be to open one socket per cookie session and then listen to and send from the socket over localStorage via an eventemitter. – paulkon Apr 20 '14 at 14:48
3

I know it has been a long time since you asked this, but Just 4 ago I published a module for node js, express and socket.io which manages that exactly thing you wanted. Check the Usage and Example, I hope you will find this module helpful!

You can install it via NPM socket.io.users This is a node js module for socket.io applications. One user per client.

Some of the usage code

var express = require('express');
var app = express();
var server = require('http').createServer(app);
var socketUsers = require('socket.io.users');
//....
socketUsers.Session(app);//IMPORTANT 
//...
var rootIo = require('socket.io')(server); //default '/' as namespace. 
var chatIo = rootIo.of('/chat');

var rootUsers = socketUsers.Users; /* default '/' as namespace.
Each namespace       has IT's OWN users object list,
but the Id of a user of any other namespace may 
has the same value if       request comes from the same client-machine-user.
This makes   easy to keep a kind of 
synchronization between all users of all   different namespaces. */

var chatUsers = socketUsers.Users.of('/chat'); //  


rootIo.use(socketUsers.Middleware());/*IMPORTANT but no errors if you want
to  skip it for a io.of(namespace) 
that you don't want the socket.io.users' support.  */

chatUsers.use(socketUsers.Middleware());

chatUsers.on('connected',function(user){
console.log(user.id + ' has connected to the CHAT');
user.store.username = 'username setted by server side'; /*at the store
property you can store any type of properties
and objects you want to share between your user's sockets.  */
user.socket.on('any event', function(data){ 
/*user.socket is the current socket, to get all connected sockets from this  
user, use: user.sockets */

});
chatIo.emit('set username',user.store.username);
});

rootUsers.on('connected',function(user){
   console.log('User has connected with ID: '+ user.id);
});



rootUsers.on('connection',function(user){
   console.log('Socket ID: '+user.socket.id+' is user with ID: '+user.id);
});

rootUsers.on('disconnected',function(user){
    console.log('User with ID: '+user.id+'is gone away :(');
});

//...server.listen blabla..
kataras
  • 835
  • 9
  • 14
2

You can use hash like that:

var sessionHash = {};
sessionHash['userName'] = socket;

and check on io.sockets.on('connection',..) whether a socket exist under that key.

If it does, you can execute sessionHash['userName'].disconnect(); on it and save the brand new socket.

ohadinho
  • 6,894
  • 16
  • 71
  • 124
  • I did like you suggested. i want to keep only one active tab for each connected user. The problem is when I try to log the old sessionID (old tab) and the new one (just opened tab), they are the same. I can't understand why, since I'm disconnecting the old socket – user1499220 Dec 19 '13 at 23:25
  • I was trying your approach, but the socket (in the first tab) is different as the socket (in the second tab), am I doing something wrong? – Sobiaholic Dec 02 '14 at 21:18
0

Intercom library can be used to share and manage a socket.io connection.

// Initialize socket.io on one tab
var socket = io.connect('//yourwebsite.com/socketio');

// Initialize intercom on all tab
var intercom = Intercom.getInstance();
intercom.bind(socket);

// Listen to an event
intercom.on('msgBroadcast', function(data){
    console.log(data);
});

// Emit an event
intercom.emit('msgBroadcast', {
    socket: true,
    message: 'Hello all!'
});

You could also use socketio shared webworkers

StefansArya
  • 2,802
  • 3
  • 24
  • 25