socketio / socket.io

Realtime application framework (Node.JS server)
https://socket.io
MIT License
60.66k stars 10.09k forks source link

Shared Websocket connections over multiple tabs #5095

Open khugin opened 10 years ago

khugin commented 10 years ago

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

rauchg commented 10 years ago

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

khugin commented 10 years ago

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.

kapouer commented 9 years ago

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.

kapouer commented 9 years ago

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.

kapouer commented 9 years ago

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

kapouer commented 9 years ago

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.

kapouer commented 9 years ago

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

fijiwebdesign commented 7 years ago

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.

ayotycoon commented 5 years ago

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

liho00 commented 2 years ago

look like shared worker not supported in some browser image

kapouer commented 2 years ago

Old browsers won't get shared connections, so what ? Actually shared worker is not even supported on latest Safari. However it seems service worker quite well supported on all recent browsers !