Socket.io-client: Shared Websocket connections over multiple tabs

Created on 24 Feb 2014  ·  9Comments  ·  Source: socketio/socket.io-client

I'm trying to find a solution to avoid multiple connections over multiple open tabs. Instead, I'ld like to share the connection between them (especially since users tend to have up to 30 tabs open sometimes). Now, I'm apparently not the only one with the Issue, and after searching around for informations for a while, I found this 2 year old entry: https://groups.google.com/forum/#!topic/socket_io/FbHU7HvxK6U

Here, Guillermo Rauch writes:

24/06/2011
I have a solution outlined which will probably land in 0.9.

Now, I'm wondering: is it implemented? Does it work? I couldnt find any informations about it, and I'ld really like to solve the problem over socket.io instead of creating a strange localStorage hack to share a connection.

Thanks in advance, I hope there are some good news - would save me a lot of work ;)

Most helpful comment

For anyone still having this issue. here is how i fixed it.
let me explain.
once the page refreshes or a new tab is opened, socket dosen't really care so it opens a new connection every time . this is more of a advantage than disadvantage. the best way to tackle the issue is on the server side, once a user logs in with his or her user name , you can send that name along with the query options on the client so it can be used as a unique identifier. in my case i used a token

this.socket = io.connect(`${environment.domain}` , {
      query: {token: this.authservice.authToken}
    });

then on the server side you can create an empty array to a key and an array of values. the username of the user will be used as a key and the corresponding array of socket as the value. in my own case like i said i used a token

const users = [ ]
socket.nickname = (decoded token username);
users[socket.nickname] = [socket];

then you can perform a simple logic to check if a user already exists in an array, if it does, push the new socket to the array of the user

if ( user.username in users) {
                    console.log('already exists')
                    users[user.username].push(socket);
                } 

if it dosent, just create a new key and add the socket as the key.(make sure its an array because a user can always refresh or open a new tab with the same account and you dont want the chat message to deliver in one tab and not deliver in another)

else {
                    socket.nickname = username;
                    users[socket.nickname] = [socket];

                }

then to emit a message you simply loop through the array and emit the message accordingly. this way each tab gets the message

socket.on('chat', (data) => {

            if (data.to in users) { 

                for(let i = 0; i < users[data.to].length; i++) {
                    users[data.to][i].emit('chat', data)
                }
                for(let i = 0; i < users[data.user].length; i++) {
                    users[data.user][i].emit('chat', data)
                }


            }
        })

you can add a disconnect logic to remove the socket from the users array too to save memory, so only currently open tabs acre active and closed tabs are removed. i hope it solved your problem

All 9 comments

Can you describe your proposed solution? I'm very interested in making this happen.

Oh, if I'ld have one, that would already be great. Sadly, it seems to be quite difficult (especially when regarding IE). I hoped / thought this was already solved.

As far as my researches / tests went, there are 3 Solutions:

  1. Using SharedWorker's, but that only seems to work on Chrome and Opera, maybe on FF, but not on <=IE9. But still, as far as I could tell, this is probably the best approach. I didnt have the time to dig into it enough tough, to see how it could be used with socket.io.
    http://www.sitepoint.com/javascript-shared-web-workers-html5/
  2. The common solution (according to many SO threads) a while ago, was to use LocalStorage (basically having one connection, that saves all events while other tabs listen from them). But I'm quite sceptic about that one - especially performance-wise. I'm still reading about Issues with LocalStorage that causes the browser to freeze - and in my case it'ld be used for a chat (so, a high event-frequency) so I guess it'ld be too slow at some point.
  3. The third "solution", and the one I'll be probably be forced to use at the end, is to simply disconnect multiple connections, after the first one. Would be a shame tough, since that's not really a solution but just a workaround to avoid multiple connections.

As far as I can tell, simply re-using the already established websocket connections over different Tab's doesnt seem to be possible - so it always has to go over a master connection in the first tab.

Sorry, I had the impression that this was already solved somehow by that post 2 years ago, that's why I was asking ;) I wish I'ld already have found a solution, but it really seems quite difficult.

Bump !
https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
the idea is to check there isn't already a socket.io instance connected on same domain using postMessage. If there is, a "simple" relay must be done between new socket.io instances and the existing one.
Being bitten currently by a stupid limitation in webkitgtk (6 xhr per host, cumulated on all tabs !) i might try to test that idea.

Also when a "host" instance has its window closed, other instances must receive a signal to decide which one is going to serve as master.

For shared web workers, i came across https://github.com/gonzalo123/wsww

The two ideas combined could be great: use a shared web worker when available, fallback to sharing first window connection if postMessage is available between windows - that's everyone but IE<=9.

Hey checkout this sweet Cross-tab message bus for browsers !

I'm working on socket.io inside a shared webworker while keeping the same API.

https://github.com/IguMail/socketio-shared-webworker

Feedback would be greatly appreciated.

For anyone still having this issue. here is how i fixed it.
let me explain.
once the page refreshes or a new tab is opened, socket dosen't really care so it opens a new connection every time . this is more of a advantage than disadvantage. the best way to tackle the issue is on the server side, once a user logs in with his or her user name , you can send that name along with the query options on the client so it can be used as a unique identifier. in my case i used a token

this.socket = io.connect(`${environment.domain}` , {
      query: {token: this.authservice.authToken}
    });

then on the server side you can create an empty array to a key and an array of values. the username of the user will be used as a key and the corresponding array of socket as the value. in my own case like i said i used a token

const users = [ ]
socket.nickname = (decoded token username);
users[socket.nickname] = [socket];

then you can perform a simple logic to check if a user already exists in an array, if it does, push the new socket to the array of the user

if ( user.username in users) {
                    console.log('already exists')
                    users[user.username].push(socket);
                } 

if it dosent, just create a new key and add the socket as the key.(make sure its an array because a user can always refresh or open a new tab with the same account and you dont want the chat message to deliver in one tab and not deliver in another)

else {
                    socket.nickname = username;
                    users[socket.nickname] = [socket];

                }

then to emit a message you simply loop through the array and emit the message accordingly. this way each tab gets the message

socket.on('chat', (data) => {

            if (data.to in users) { 

                for(let i = 0; i < users[data.to].length; i++) {
                    users[data.to][i].emit('chat', data)
                }
                for(let i = 0; i < users[data.user].length; i++) {
                    users[data.user][i].emit('chat', data)
                }


            }
        })

you can add a disconnect logic to remove the socket from the users array too to save memory, so only currently open tabs acre active and closed tabs are removed. i hope it solved your problem

Was this page helpful?
0 / 5 - 0 ratings