Socket.IO not getting disconnect event on polling - node.js

My browser is defaulting to "polling" method, causing me not to get the disconnect event on the server side.
I've tried the solution covered in socket.io force a disconnect over XHR-polling but this didn't do the trick for me:
Server.socket = io.connect("https://somedomain:8443", {"sync disconnect on unload":true, secure:true});
How can I track users leaving my server with polling?

so, apparently for some reason the heartbeat timeout was too long for me to think the disconnect mechanism was working.
I changed the timings:
var io = require('socket.io')(server, {'pingInterval': 4000, 'pingTimeout': 8000});
In the server, and after 8 seconds, sure thing, I get the disconnect event.
The low numbers are because I'm creating a multiplayer game...

Related

Node.JS net module handling unexpected connection loss

I can't figure out one problem I got.
I'm using the Net module on my Node.JS server which is used to listen to client connections.
The client do connect to the server correctly and the connection remains available to read/write data. So far, so good. But when the client unexpectedly disconnects (ed. when internet falls away at client side) I want to fire an event server side.
In socket.io it would be done with the 'disconnect' event, but this event doesn't seem to exist for the Net module. How is it possible to do?
I've searched on Google/StackOverflow and in the Net documentation (https://nodejs.org/api/net.html) but I couldn't find anything usefull. I'm sry if I did mis something.
Here is a code snippet I got:
var net = require('net');
var server = net.createServer(function(connection) {
console.log('client connected');
connection.wildcard = false;//Connection must be initialised with a configuration stored in the database
connection.bidirectional = true;//When piped this connection will be configured as bidirectional
connection.setKeepAlive(true, 500);
connection.setTimeout(3000);
connection.on('close', function (){
console.log('Socket is closed');
});
connection.on('error', function (err) {
console.log('An error happened in connection' + err.stack);
});
connection.on('end', function () {
console.log('Socket did disconnect');
});
connection.on('timeout', function () {
console.log('Socket did timeout');
connection.end();
});
connection.on('data', function (data) {
//Handling incoming data
});
});
serverUmrs.listen(40000, function () {
console.log('server is listening');
});
All the events(close, end, error, timeout) don't fire when I disconnect the client(by pulling out the UTP cable).
Thanks in advance!
EDIT:
I did add a timeout event in the code here above but the only thing that happens is that the socket does timeout after 3 seconds everytime the client does connect again. Isn't KeepAlive enough to make the socket not Idle? How is it possible to make the socket not idle without to much overhead. It may be possible that there are more than 10,000 connections at the same time which must remain alive as long as they are connected (ie respond to the keepalive message).
Update:
I think the KeepAlive is not related with the Idle state of socket, sort of.
Here is my test, I remove the following code in your example.
//connection.setKeepAlive(true, 500);
Then test this server with one client connect to it var nc localhost 40000. If there is no message sending to server after 3 seconds, the server logs as below
Socket did timeout
Socket did disconnect
Socket is closed
The timeout event is triggered without KeepAlive setting.
Do further investigation, refer to the Node.js code
function onread(nread, buffer) {
//...
self._unrefTimer();
We know timeout event is triggered by onread() operation of socket. Namely, if there is no read operation after 3 seconds, the timeout event will be emitted. To be more precisely, not only onread but also write successfully will call _unrefTimer().
In summary, when the write or read operation on the socket, it is NOT idle.
Actually, the close event is used to detect the client connection is alive or not, also mentioned in this SO question.
Emitted when the server closes. Note that if connections exist, this event is not emitted until all connections are ended.
However, in your case
disconnect the client(by pulling out the UTP cable).
The timeout event should be used to detective the connection inactivity. This is only to notify that the socket has been idle. The user must manually close the connection. Please refer to this question.
In TCP connection, end event fire when the client sends 'FIN' message to the server.
If the client side is not sending 'FIN' message that event is not firing.
For example, in your situation,
But when the client unexpectedly disconnects (ed. when internet falls away at client side) I want to fire an event server side.
There may not be a 'FIN' message because internet is gone.
So you should handle this situation in timeout without using keepAlive. If there is no data coming data, you should end or destroy the socket.
EDIT: I did add a timeout event in the code here above but the only
thing that happens is that the socket does timeout after 3 seconds
everytime the client does connect again. Isn't KeepAlive enough to
make the socket not Idle? How is it possible to make the socket not
idle without to much overhead. It may be possible that there are more
than 10,000 connections at the same time which must remain alive as
long as they are connected (ie respond to the keepalive message).
For your edit, your devices should send to the server some heartbeat message between a time period. So that, server understands that that device is alive and that timeout event will not fire because you get some data. If there is no heartbeat message such cases you cannot handle this problem.

Socket.io takes a long time to connect

I'm writing a node.js socket.io websockets application (version 1.3.7 of socket.io), and about 75% of the time the client takes a long time to connect to the server - the other 25% of the time it connects pretty much instantly. I've enabled debugging on both the server and the client, and it hangs in both places at the same spot:
Server Log
Client Log (Chrome)
Eventually it will connect, and I've been able to make it connect faster by reducing the timeout from the default of 20 seconds to about 5 seconds, but I'm not sure why it's hanging in the first place. Watching the Chrome network tab, it seems like when a connect attempt is made it will either work immediately or it won't work for the rest of the connect attempt. So dropping the timeout to 5 seconds just means it will make more attempts faster, one of which will eventually succeed.
Network Log (Chrome)
In this case it took 5 connection tries, about 20 seconds, to connect.
Client Code
// client.wsPath is typically http://127.0.0.1:8080/abc, where abc is the namespace to connect to.
client.socket = io.connect(client.wsPath, {timeout: 5000, transports: ["websocket"]});
Server Code
var express = require("express");
var io = require("socket.io");
var htmlApp = express();
var htmlServer = http.Server(htmlApp);
htmlServer.listen(DISPATCH_SERVER_LISTEN_PORT, function()
{
log.info("HTML Server is listening on port " + DISPATCH_SERVER_LISTEN_PORT);
});
var wsServer = io(htmlServer, {transports: ["websocket"]});
var nsp = wsServer.of("/" + namespace);
nsp.on("connection", function(socket)
{
log.info("connect");
};
We've found that clearing the browser cookies can help, but doesn't seem like a permanent solution - is there something that I'm doing wrong?
We are facing similar issue with socket IO SDK. It seems the SDK is actually waiting for the acknowledgement(util is receives the message with SID) to start messaging. But in the typical WS/WSS communication we can start messaging immediately after the connect. We are trying to tweak the SDK in such a way that it can start messaging immediately after connection establishment. Please share if any one has found a better approach.
Anybody else still facing this error?
It is happening to a server I deployed, these are the versions:
"socket.io-client": "^4.5.1"
"socket.io": "^4.5.1"
Sometimes it connects instantly, but others it takes around 1~2 minutes

socket.io client reconnect timeout

I'm using socket.io with node.js and I like the solution. The only issue I notice is around a disconnection and reconnection.
These are my current settings:
'connect timeout': 1000,
'reconnect': true,
'reconnection delay': 300,
'max reconnection attempts': 10000,
'force new connection':true
I notice if I stop and start the node.js process the client connects back fine and quickly, however if the server is offline for a couple of minutes the client either never reconnects or takes a very long (non-user friendly) amount of time to.
I wanted to ask if there is anything I've missed or could add to the socket.io configuration to keep the client polling for a reconnection.
I know 'reconnection delay':
reconnection delay defaults to 500 ms
The initial timeout to start a reconnect, this is increased using an
exponential back off algorithm each time a new reconnection attempt
has been made.
But the exponential effect its not very user friendly. Is there a way to keep checking for a connection every X period of time - eg: 5 seconds.
If not I guess I can write some client side JS to check the connect and attempt reconnections if needed but it would be nice if the socket.io client offered this.
thx
There is a configuration option, reconnection limit (see Configuring Socket.IO):
reconnection limit defaults to Infinity
The maximum reconnection delay in milliseconds, or Infinity.
It can be set as follows:
io.set("reconnection limit", 5000);
When set, the reconnection delay will continue to increase (according to the exponential back off algorithm), but only up to the maximum value you specify.
EDIT: See answer below for proper approach
I am afraid that the reconnection algorithm cannot be modified (as of December 2013); the Github issue to allow this feature is not merged yet. However, one of the commentors is suggesting a small workaround, which should nullify the exponential increase:
socket.socket.reconnectionDelay /= 2 on reconnecting
The other approach is, as you said, to write some client-side code to overwrite the reconnecting behavior, and do polling. Here is an example of how this could be done.
EDIT: the above code will have to go inside the 'disconnect' event callback:
var socket = io('http://localhost');
socket.on('connect', function(){
socket.on('disconnect', function(){
socket.socket.reconnectionDelay /= 2;
});
});
This is the solution I went with - when the socket disconnects it enters a loop that keeps trying to reconnect every 3 seconds until the connection is created - works a treat:
socket.on('disconnect', function() {
socketConnectTimeInterval = setInterval(function () {
socket.socket.reconnect();
if(socket.socket.connected) {clearInterval(socketConnectTimeInterval);}
}, 3000);
});
cheers

How to make the client disconnect immediately on network loss in socket.io?

I am developing a chat application using socket.io for iOS app and androdi app. And the issue am facing is when network connection is suddenly lost on client, it takes 25 secs(default heartbeat interval) for the disconnect event on the socket.io server to get called. I am removing the socket.id/username from my db when the disconnect event is called. But since during this 25 secs the socket.id/username still exists on my db, when some other user sends a message to this user who lost network connection, it will be shown as sent even though it was never delivered. The solution would probably be to reduce the Heartbeat interval to maybe 1 or 2 secs. I have tried that but strangely its not being set, it still takes 25 secs approx. Below is my code
var app = require('express')()
, server = require('http').createServer(app)
, sio = require('socket.io')
, redis = require('redis');
var client = redis.createClient();
var io = sio.listen(server, { origins: '*:*' });
io.set("store", new sio.RedisStore);
io.set("heartbeat interval", 2);
var azure = require('azure');
server.listen(4002);
But even if i get the heartbeat interval set to 2secs, i think there might be a downside to it. If the mobile apps send acknowledgement to the server's heartbeat every 2secs then taht would probably drain the app's battery if the app is left idle for a long time. So I would appreciate any solutions at all that would work best in my case.
Thanks
there are many variables to consider, when to disconnect a client , since 25secs is a way to small interval to check something especially if you application has say 100.000 users.
Things you could try
client side control, to see if a user is typing or even touching the device to get user status idle|active|zombie.
don't remove user from redis, immediately after client emit disconnect, instead you could place the user , into a toDisconnect queue list, or set an additional variable, if the same user is connected again then you can simply move the object, instead of querying again for creating the object.
if still not satisfied with result, try binary websockets, at least it will remove some overhead from packets and they will be delivered a bit faster, since the size is a smaller.
bottom line, don't rely on redis, you could easily remove it from structure, and apply a custom websocket server, and all user management build in node.js

socket.io xhr-polling disconnect event

I have a socket.io node script with:
socket.on('disconnect', function(data) {
console.log('disconnect!');
});
When I connect with Chrome / Safari and close the page, I see 'disconnect!' in my server console.
However, when I connect with my iPhone and close the page, I don't see this message. I see debug - xhr-polling closed due to exceeded duration
How do I receive the disconnect event with iOS?
Socket.io switches to the xhr-polling transport when you are viewing the page in your iPhone. This might be caused by the configuration of socket.io or because the browser in your iPhone does not (fully) support websockets.
The xhr-polling implementation in socket.io does not emit disconnect event when the connection is closed, see github issue #431. You can reproduce this issue in your Chrome browser by forcing the socket.io server to use the xhr-polling transport only:
// the server side
var io = require('socket.io').listen(httpServer);
io.set('transports', ['xhr-polling']);
Good news: you can ask socket.io's client to notify the server about disconnect by turning on the sync disconnect on unload flag:
// the browser (HTML) side
var socket = io.connect('http://localhost', {
'sync disconnect on unload': true
});
Warning: this option can worsen the user experience when the network and/or your server are slow, see this pull request for more information.
UPDATE
According to socket.io force a disconnect over XHR-polling, setting sync disconnect on unload might not be enough to fix the problem on iPhone/iPad.
As you can see in socket.io-client source code, sync disconnect on unload sets up a listener for beforeunload event, which is not supported by iOS Safari according.
The solution is probably to fix socket.io-client to listen for both unload and pagehide events, because
the unload event may not work as expected for back and forward optimization. Use the pageshow and pagehide events instead. [Apple Web Content Guide].

Resources