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
Related
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...
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.
In our redis configuration we have set timeout: 7 seconds
In node_redis We handle redis connection ready and end event as
client.on("ready", function() {
logger.info("Connection Successfully Established to ", this.host, this.port);
}
client.on("end", function() {
logger.fatal("Connection Terminated to ", this.host, this.port);
}
Sample log
[2012-07-11 08:21:29.545] [FATAL] Production - Connection Terminated
on end to 'x.x.x.9' '6399'
[2012-07-11 08:21:29.803] [INFO] Production - Connection Successfully Established to 'x.x.x.9' '6399'
But in some cases (most probably redis is closing the connection without notifying the client) we see the command queue getting piled up and requests are taking too much time to get the response [till the time node-redis client able to sense the close event]. In all such cases command callback is returned with this error Redis connection gone from close event. even after so much waiting. It looks as if this is not an issue because of timeout since the usual end event wasn't triggered.
Issue seems to be similar to this - http://code.google.com/p/redis/issues/detail?id=368
Is this a known thing happening in redis?
Is there a way to specify that execution of a command [sending and receiving a reply back] should not exceed the threshold and reply with an error in that case, instead of making the client stall?
Or is there anyother way of triggering close event in such cases like socket_timeout?
Or should we check something from our redis side? We monitored our redis log at debug level and we found nothing useful related to this issue
When we run the node-redis on debug mode we are clearly able to see the client getting stalled with the requests getting piled up in the command queue. We logged the why and queue length inside flush_on_error function. We have kept offline_queuing disabled.
Sample Log
Redis connection is gone from close event.
offline queue 0
command queue 8
Response time of failed a request: 30388 ms [this varies as per the waiting in the command queue. First queued guy has the max response time and the ones following him lesser]
Usual Resonse time: 1 ms
PS: We have filed an issue in node_redis too
We had a bunch of connection trouble with Redis as well. It seems like it would close the connection without it telling the client. We noticed that it was possibly a timeout problem on the server. This is the solution that we use and we haven't had a problem since July.
var RETRY_EVERY = 1000 * 60 * 3;
var startTimer = function(){
console.log('Begin the hot tub!')
setInterval(function(){
try{
client.set('hot',new Date());
console.log(client.get('hot'))
}
catch(e){
console.log(e);
}
},RETRY_EVERY)
}();
Considering it's only one call every 3 minutes, it shouldn't be a problem for performance ;)
With regards to oconnecp's answer, can't you just do:
setInterval(client.ping(), 1000 * 60 * 30);
I have a basic Socket.io server setup like this:
var server = express.createServer().listen(port);
this.io = socketIO.listen(server);
this.io.sockets.on('connection', function(socket){
initSocket(socket);
});
I also have socket.io configured to use XHR polling like so:
io.set("transports", ["xhr-polling"]);
io.set("close timeout", 3);
io.set("heartbeat interval", 2);
The issue is every time the client sends a heartbeat (every 2 sec), the 'connection' event is being fired. Is there a different event I can use that will fire once each time a socket initially connects?
I would use the "authorization" event, but that only passes in a handshake object not the actual socket.
Found the problem. I had my xhr "polling duration" set to heroku's suggested 10s like so:
io.set("polling duration", 10);
Which means that the client only makes a new xhr request every 10 seconds (as soon as the previous request returns). At the same time I had the "close timeout" set to 3 seconds:
io.set("close timeout", 3);
Which means if the server does not hear from the client within 3 seconds since its last request it closes the connection, hence the continuous 'disconnect/connection' events being fired.
The solution was to set the close timeout higher than the polling duration. It would seem that the "heartbeat interval" is not relevant for xhr connections.
Are you setting too low a value for closetimeout and heartbeat interval. The default values for these are 60 sec and 25 sec. Typical network lag is of the order of 5 sec. So with the values that you have set, the client does not respond in the time specified, so the connection is closed. When the client connects again, the connection event is fired.
In production, I have a game which uses connection-local variables to hold game state. However I notice that if I idle for a certain time on the connection, it disconnects and reconnects which loses the current state. During my tests on a local host, I never noticed this behavior. Is this the norm behavior for socket connections or is something else causing the connections to drop.
If it is a normal behavior how is this typically handled? Should connection values be stored globally so they can be restored should a user drop/reconnect?
Your problem is around socket timeouts. If there's no activity on a certain socket, socket.io will close it automatically.
An easy (and hackish) fix is to send a heartbeat to the connected client to create activity and stop the socket from timing out.
Server:
function sendHeartbeat(){
setTimeout(sendHeartbeat, 8000);
io.sockets.emit('ping', { beat : 1 });
}
io.sockets.on('connection', function (socket) {
socket.on('pong', function(data){
console.log("Pong received from client");
});
}
setTimeout(sendHeartbeat, 8000);
Client:
socket.on('ping', function(data){
socket.emit('pong', {beat: 1});
});
More Information:
You can get more information on configuring socket.io here.
EDIT: Mark commented that if the user does lose the connection (connection drops on his end because of internet troubles), you should be able to restore the user to his last state.
To do that, the best way would be to use a already widely used method for storing user data, cookies and sessions.
An extremely well done tutorial on how to do this located here. Although he uses express to set cookies, you can do this using anything (I do it using rails). Using this method, you can store the user data in a cookie and fetch it during the handshake. From there you can just access the data using socket.handshake.data.
What you need to do is create or identify the session per (re-) connection. You may reduce the number of reconnections per Moox's answer above but it is still not failsafe - e.g. a user loses wifi connection for a bit, etc. In other words - maintain user metadata per session and not per socket, and expect occasional disconnects and reconnects.