Socket.io receives messages before browser navigates to new page - javascript

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:
info - handshake authorized Q9syoIK47JI7dACYpxiA
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:
User is on a page connected to sio session 1.
We begin navigation to new page. On 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.
A new page is loaded. It will connect to the Socket.io server and be assigned a new session ID, 2.
Anything you send to session 1 will obviously not be delivered since our client is now connected to session 2.
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:
Since you're doing a full page load, you can send queued notifications directly down with the page itself. Delete the queue when you've successfully rendered.
On a new Socket.io connection, send unread notifications over the socket. Give .emit a 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.

Simplest solution: Use the onbeforeunload event to disconnect socket.io.
I verified with a HTTP debugger that this event fires before the browser issues the request (at least in Chrome) so if socket.io is disconnected here, it won't receive any messages meant for the following page.
window.onbeforeunload = function() {
socket.disconnect();
};
I haven't tested this in multiple browsers, so it might not be a perfect solution, but it seems to solve the problem for now.

Related

Socket connection only when needed

I have a web application that needs to refresh some values ​​often because the changes have to be available almost in real time. To do this, I run via ajax a refresh.php routine every 15 seconds, which returns the updated information. Time that increases if there is no user activity.
I thought about the possibility of creating a service-worker in the browser (since I already use it for pwa too), and in it create a web-socket, and then only when there is an update on the server, create a socket for the ip's of the users that are logged in (and saved in a db), just to send to the user's browser that there is an update, then the web-socket triggers the javascript routine that connects to the server and does the update.
I do not know if it would be possible to create the socket just to inform that there is an update, because in this case, I do not want to leave the socket open, creating the connection only when there is an update of the information, which should happen to several users.
Has anyone ever needed or done anything like this, or would you have any other ideas?

How to logout user after disconnect or browser close?

I'm trying to work a solution to timing out a user from my web application. I'm currently using ng2-idle and it seems to only work on the active window rather than be tracked server side (angular server webpack)
I need to handle these two events in addition to the one above:
On Browser Close
On Connection loss (Power cut, blue screen, etc..)
After testing, my timeout was not being tracked after closing the window. Ng2-idle has modules such as keepalive but I'm not exactly sure how to use it and if it solves my problem
I will provide code if needed
thank you
The main problem is, that the client and server are communicating in stateless manner. This means if the user disconnects no body knows.
If your browser has a hook function for closing or navigating to another site you could use that and send the logout request.
Another thing which relavant is session expiration, you should use that. If you are using a token you will need to blacklist that, as non active as long as the session may be valid (or however).
Disconnect is a major problem (session expiration tried to solve that somehow).
A more sophisticated way if it is really crucial to log out on disconnect you may need to use websockets or http long polling. You would need to send a heartbeat and if it's not responding, after some time you will automatically logout the user.
Hope these thoughts kind somehow help.
when you close your browser session will destroy.So you can use session to logout.

Pubnub: Background Processes for an Auction App Development

Hello I am developing an auction app like tophatter.com. I want to implement an application that has background process in it. I want this process to run forever until I stop it
http://eoction.com thatss our current site. The problem on our site when we refresh the page the auction also restart. We need something like a continuous process like tophatter.com if you refresh the page it will load the updated auction process.
I found this great service called pubnub. I am thinking we need a background process for this? This will process the auction on the pubnub blocks and then when we visit the site we will just need to query on its updated process?
Does pubnub support something like this?
PubNub Web Page Best Practices
When user refreshes your web app page or navigates to another page there are things you need to consider as a web app developer no matter what technologies you may be using. I will address, at a high level, the things you need to do when PubNub is integrated into your web page.
Restore Parameter
Whether the user interrupts your connection to PubNub or it is a network failure, you will want PubNub to reconnect and continue where it left off as much as possible. The PubNub JavaScript SDK has a initialization parameter called restore that when set to true, will reconnect to PubNub and get missed messages after the connection is dropped and reestablished.
var pubnub = new PubNub({
subscribeKey: "mySubscribeKey",
publishKey: "myPublishKey",
ssl: true,
uuid: getUUID();
restore: true
});
Reuse UUID
It is important to reuse the same UUID for each end user as this will allow PubNub to identify that user uniquely when it comes to Presence so that it doesn't produce new join events for the same end user. The PubNub JavaScript SDK actually generates a UUID and stores it in localStrorage and reuses it by default but very likely you have your own UUID that you would like to use for each of your end users.
Last Message Received Timetoken
If the network disruption is brief as is the case with a page refresh or page navigation, then missed messages are retrieved when restore:true is implemented in the init as stated above. But when the user is offline for more than say 5 minutes, you may want to retrieve missed messages on one or more channels. The best way to do this is to keep track of the timetoken of the last received message by storing it in localStorage every time a message is received via subscribe callback. When the user comes back online and it is has been more than 5 minutes since they were last online, call history using this last received message timetoken on each channel that you need to get missed message from.
Subscribe to Channels
Finally, you'll want to make sure that the user is subscribed to the channel they expect to be based on what their state prior to the connection disruption. If it is a page refresh, you likely just want to resubscribe them to the same list of channels. To do this, you just need to keep a list of channels they are currently subscribed to, once again, in localStorage. If the user navigates to a new page and this causes a full page reload (modern web apps should not require this, but...) then you may want to unsubscribe from some channel(s) and subscribe to new channel(s), it just depends on what that page navigation means to your app. Modern web app frameworks do not require full page reload for page navigation since the web app acts more like a desktop app than older web apps. And again, if the the user was offline for quite some time (more than 5 minutes) then it may not make sense to subscribe them to the same channels that they were subscribed to before. Really depends on your use case.
And by the way, Tophatter uses PubNub ;) but all of the above are generic best practice guidelines and recommendations and is not referencing any one app in particular.
EDIT: To address you question specifically, as pointed out in comments below...
You can't implement long-running process in PubNub BLOCKS (not currently, anyways), so you will need a server process for this. When the user refreshes the page, you just need to hit your server for current state. If using PubNub to keep this progress bar updated in realtime, you just subscribe to that channel that is sending the state of that progress bar and update your client. Using the same best practices I provided above are still necessary.

How can I check whether some specific user has an internet connection?

I maintain a system built with Ruby on Rails. The client has asked me to prevent the system from working when a specific user has no internet connection.
How would you do it?
I need to check if a specific computer has internet connection!
For instance if my pc or your pc or my mother's pc has no internet connection!
This has nothing to do with Ruby on Rails.
Is this user accessing a site that's on a local network but no outside internet access? If so you can have a before_filter or some kind of method on your homepage that tries to ping google.com or some other site outside of the LAN to check for internet.
Use Offline.js
Add offline.min.js file in your vendor/assets/javascripts
Require it in your app/assets/javascripts/application.js as //= require offline.min
Add the following code in the same application.js file
$(document).ready(function() {
Offline.check();
Offline.on('up', function() {
$('a.btn').removeClass('disabled');
});
Offline.on('down', function() {
$('a.btn').addClass('disabled');
});
});
This is in relevance to my code where I just disable all buttons when the connection goes off.
The default behaviour of Offline.js adds a overlay with a please wait message until the internet connection comes back on.
I prefer to handle it this way as it give better user experience. Disabling buttons is a subtle way of preventing a user from working.
So just to clarify, your Rails application exists for users to send food delivery requests to one specific user -- the shop preparing and sending that food out, presumably -- and you want users to be unable to create new delivery requests when the shop/owner isn't currently online?
The simplest way to implement this would be to have an Admin Dashboard area where the owner can click a button to 'open the shop' and allow new orders. The worry, then, is about the shop's connection going offline, resulting in customers whose order are never received. To get around that, you can have each customer order start in an 'unconfirmed' state; when a new order is submitted, the owner is notified, and he can click a button to confirm it, letting the user know that yes, their order has been seen and is being made (and their credit card can be charged then, if you have online payment). This is probably the most reliable solution because what you really want is confirmation that a human has acknowledged the order.
If you do want to tie it directly to online status, then you can have the Admin Dashboard page regularly check that it's being viewed. You could accomplish this with a websockets connection, see Action Cable introduced in Rails 5. The Admin Dashboard page opens a websocket connection upon login; the new-connection event sets accepting_orders = true. The websocket disconnect event sets accepting_orders = false, which will happen soon after their connection is dropped (make sure to check the timeout settings). Without websockets, you could have a background HTTP request occurring on an interval; x seconds without receiving that request could cause Rails to stop accepting new orders.

Websocket with AngularJS/Asp.net

I had a specific questin about angularjs with websocket. I currently have an application that utilizes a websocket to communicate with a server, this is all nice and dandy - and when I move around pages in angular the websocket persists throughout all of the routes which is neat. Unfortunately the problem is that if the user refreshes the page (for some dumb reason), the websocket disconnects. I was wondering what the best method of handling this is. Should I just have an alert when the user tries to refresh, can I somehow detect that the websocket is closed when the page is refreshed and start a new one? I'm just wondering what the best practice for something like this is.
Thanks
There is nothing you can do, if the user refreshes, it is like restarting an application, all the bootstrapping happens again and connections are created again.
You can use javascript:onbeforeunload to warn the user that if refreshes or leaves he will lose the connection. But your users will hate your for that, it is very annoying.
Consider as well, that the user may open several tabs.
Starting a new connection is the best way. Just make sure that the user can somehow recover his context. If there is a different context per tab, then you will have to put a connectionID parameter in the URL to persist it through refreshes, and if the context is per user session, then a cookie with the session ID will do.

Categories