I am trying to make a simple socket.io game. I have a login screen, where you enter your username and password, and then you click a big connect button and then you're in (if the client manages to connect to the server). I would like to have a status box on the login screen, so you can see if the server is up or down. I am wondering if there is some sort of ping function in socket.io. Otherwise, could you try to connect to the server (for example, I don't know what's good) 4 times. If it fails, it calls a function/sets a variable, whatever. If it succeeds it calls a function or something, then it disconnects. I have googled around a bit, but can't find anything. (By the way, sorry if this is really noobish, and there is a really apparent solution. I'm new to socket.io 😅) All help will be deeply appreciated!
Socket io has an event listener for this situation. Reconnecting event will fire when socket.io tries to connect but fails. Its a bit misleading cuz it won't fire on every reconnect just when the reconnect try fails.
socket.on('reconnecting', function reconnectCallback(tries) {
if (tries === 3) {
//handle your offline mode here
}
});
socket.on('connect', function connectCallback() {
//handle successful connection here then disconnect
socket.disconnect();
});
Related
I have some node.js client side code like this:
socket.emit('clickAccept', { myrecid: recid });
Server side node.js code gets it fine and all is well.
If I take the server down to simulate a server side outage, then click the button that fires this socket.emit on the client side, this happens:
Nothing really, I guess it might eventually time out
When I bring the server back up, the clicks end up being sent to the server and the server acts on them (TCP-like I Guess).
What I want to happen is for those socket.emit calls to die after a short timeout and not send when the server comes back up, it causes all sorts of confusion because if they click 3 times, nothing happens, then when/if the connection or server comes back up they get 3 reactions all at once.
Also, if they click and it times out because the server is down, I would like to show an error to the client user to let them know that basically the click didn't work and to try again.
I know how to act on and show an error if the socket goes down but I don't want to do this if they aren't trying to click something at that time. No sense is firing errors at the user because the socket went down briefly if they have no need to do anything at that moment.
So, to be clear, I only want to show an error if they click on the button and the socket between the client and server is down. AND... If they get an error, I want to kill that emit, not save it all up and fire it and all the other clicks when the server comes back up a few seconds later.
Thanks in advance and I hope that was at least reasonably clear.
The root of your issue is that socket.io attempts to buffer any data that it can't currently send to the server (because the connection to the server is disconnected) and when the server comes back up and the connection is restored, it then sends that data.
You can see the technical details for how this works here: socket.io stop re-emitting event after x seconds/first failed attempt to get a response
You have several implementation options:
If socket.io already knows the client is not connected to the server, then don't buffer the data (perhaps even give you back an error to show to your user).
When socket.io reconnects and there was data buffered while the connection was down, clear that data and throw it away so old data isn't sent on a reconnect.
Implement a timeout to do one of the above after some sort of timeout.
So, to be clear, I only want to show an error if they click on the button and the socket between the client and server is down. AND... If they get an error, I want to kill that emit, not save it all up and fire it and all the other clicks when the server comes back up a few seconds later.
Probably, the simplest way to do that is to implement a version of what is shown in the above referenced answer:
Socket.prototype.emitWhenConnected = function(msg, data) {
if (this.connected) {
this.emit(msg, data);
return null;
} else {
return new Error("not connected");
}
}
Then, switch your code from using .emit() to use .emitWhenConnected() and check the return value when using it. If the return value is null, then no error was detected. If the return value is not null, then there was an error.
Thanks for the other answers and help. I ended up solving this in a super simple way. See below:
if (socket.connected){
// Do your thing here
} else {
// Throw error here that tells the user they're internet is likely down
}
Hope this helps someone out there, it was a huge improvement in our code to make sure that user's are getting proper feedback when if they have brief network/internet outages.
I'm using this AutobahnJS code in node to receive data from a service. It works great, getting multiple events per second. When my internet temporarily disconnects Autobahn does not detect the lost connection and does not write "Websocket connection dropped" to console, it just hangs. Indefinitely.
Is there a timeout one can set, if no data arrives after 1 minute, reconnect? Or can I use a setTimeout function to ping a server and if no pong returns close the connection and try to reopen it?
I googled till my fingers were bleeding, but I didn't find a straightforward answer to this question. Thank you very much!
connection.onopen = function(session) {
session.subscribe(arg, someEvent);
}
connection.onclose = function() {
console.log("Websocket connection dropped");
}
connection.open();
It is not possible to recognize an unclean disconnect without some data being sent. The WebSocket ping/pong mechanism at the protocol level is not exposed in the browser, and Autobahn|JS does not have any different handling when running in Node.js.
For the time being, you need to implement your own ping/pong mechanism at the application level.
I have the following abridged code:
io.on('connection', function(client) {
client.uuid = uuid.v4();
// add client object to server...
console.log('socket.io:: client connected ' + client.uuid );
client.on('disconnect', function() {
console.log('socket.io:: client disconnected ' + client.uuid );
// remove client object from server...
});
});
If I open up this page in the browser, everything seems fine. If I refresh the page, it will fire disconnect and then connection. However, if I refresh fast enough, there are times where disconnect doesn't get called and thus client data doesn't get cleaned from the server. Is there any way to protect from this?
Edit: reword reconnect -> connection
As adeneo mentioned, socket.io has heartbeats which automatically check to see if a client is still connected. The disconnect code is fired if it detects the client is actually gone. After replicating my original issue, I tried leaving the server on, and about 30 seconds later, the "dead" clients were being removed. So to solve the issue, you just have to wait. Socket.io takes care of everything on its own.
The same question was answered here.
TL;DR
You can use those options on the client:
const socket = io({
transports: ['websocket'],
upgrade: false
});
This will prevent socket.io from using the HTTP polling method at all, which causes the issues. I used this trick successfully on v4.
I'm trying to find the definition of connect_timeout, when is it fired, what's the use for it?
Reading here : http://socket.io/docs/client-api/#manager(url:string,-opts:object)
Right now I have an app I tried to run without turning on the server, and it tries to connect and the event "Reconnecting" is fired for 4 attempts, one every 2 seconds. Then it says "Failed Reconnecting" when it hits the 4 attempt mark and fires the event "reconnect_failed".
I haven't been able to hit the connect_timeout event. How do I do that? When does it happen?
I was hoping Socket.IO had some sort of function of "CONNECTING" and then if it failed it would continue attempting "CONNECTING" and if that failed it would say "CONNECTION FAILED" and if it connected successfully at some point, it would then call "RECONNECTING" instead and if that failed after a certain amount of attempts it would say "RECONNECTING FAILED". Is that something that has to be programmed by me? I haven't seen it built in.
Connection timeout is when a client is connected to the server and it takes too long for a response to be received, causing the client to disconnect because it has stopped receiving anything from the server. This can be caused by faulty internet or if the client loses connection to the server (i.e. the clients internet becomes disconnected). This is true for most server based communications and is likely the same for socket.io.
As we all know, when you update a nodejs file and upload it to your server, it needs a restart to be applied -- but if you're using something like nodemon where it automatically restarts your server, your clients that are logged in would need to refresh the screen.
So I was wondering, is there a way I could trigger a "logout" when my server goes down/is restarted so that at the minimum it just refreshes the clients so they know what's happened?
As it stands they'll broadcast undefined errors because theyre session has been lost?
I tried to validate using a function I have called authenticate
function authenticate(req,res,next) {
if (!req.session.loggedIn) {
fs.readFile('./html/login.html', "utf8", function(err, html) {
res.send(html);
});
} else next();
}
and here's how I call the function:
app.io.route('move', authenticate, function(req) {
But my server crashes because the parameters are undefined at that point.
Basically think of it like an onbeforeunload event but instead of the browser, the server.
The approach I would take is have something like Socket.IO notify all connected clients that your server is performing a restart, this restart notice, once received, would cause the client to disconnect the socket and poll for the server to come back up. Once it's back up, you can do a hard or soft refresh.
Server Server
SIGINT/Exit received --> io.broadcast("server:restart") --
Client Client
-> receive "server:restart" --> disconnect socket, poll for server back up --
Client
-> load refreshed data
You can capture some signal events like shown here and based on some information here you can use the "exit" event.
process.on("exit", function() {
app.io.sockets.emit("server:shutdown");
});
This will only work if the emit process is syncrhonous (and it looks like it may be). That means you can't get a callback you can only broadcast the message and let the client do the rest.
If you want to "hard refresh" you can poll a "heartbeat" route that just returns if the server is good (and running, obviously) and then do a location.reload() call. For a soft refresh, ping a route that returns new page contents and then page swap ($("body").html(response)).