I’m having an issue with Socket.io receiving messages just before a page navigation happens – generally when the message is a direct result of some server-side action triggered by the navigation.
What I’m seeing right now looks like this:
- Socket.io connects
- User triggers a page navigation (submits a form, refreshes, etc.)
- Server-side logic sends a request to the socket.io server, which immediately dispatches the event to the still-connected client
- The client receives and confirms the request (I’m pretty sure there’s some confirmation of messages built-in to socket.io, correct me if I’m wrong), and would display a notification to the user, but then …
- The socket.io connection closes on the original page
- The new page loads and displays
- Socket.io opens a new connection, but there’s no new messages, because the last one was received and confirmed.
This isn’t really a bug, per se, since I don’t think it’s reasonable to expect Socket.io to close the connection in advance of a navigation occurring. However, I’m not really sure what the best way to handle this is. Currently I keep one connection open per-client at a time, and close the other when a new one connects. This doesn’t happen in this case though, since the first one has closed before the second one connects. I also could keep a list of all clients, but that wouldn’t solve this problem either, because the message would still be received by the first connection.
Can someone suggest a solution to this problem that would ensure the user always sees a notification for the message?
Socket.io tracks logical connections with its own session IDs. If you watch the console when a client connects, you’ll see the IDs:
What’s important to understand is that those IDs are per-page, and completely separate from HTTP sessions. The Socket.io client library simply holds its session ID in a JavaScript variable. Therefore, upon navigation, the ID is obviously lost.
So, upon navigation, this happens:
1.window.onbeforeunload, Socket.io initiates a synchronous XHR request to tell the server it is disconnecting. If it succeeds, the session (1) is immediately terminated; otherwise, the session will eventually time out.2.1will obviously not be delivered since our client is now connected to session2.With the base Socket.io functionality, it is impossible to distinguish between a user who navigates between pages and a new user. In either case, the user will connect to a new Socket.io session.
Without knowing exactly how your app works or what you’re trying to accomplish, it’s hard to give a definitive recommendation for solving your problem.
Most likely, what you need to be able to do is associate a Socket.io session with the user’s HTTP session. You can store notifications in a queue in the user’s session, and delete them when they are displayed. There are two ways of doing this:
.emita callback function – this is the confirmation of delivery that Socket.io provides you. When delivery is confirmed, you can delete the notification queue from the user’s HTTP session.