Socket.io: client-side disconnect is not detected

Created on 11 Nov 2011  ·  58Comments  ·  Source: socketio/socket.io

var socketio = io.connect(socketServer, 
                {rememberTransport: false, 
                'reconnect': true,
                'reconnection delay': 500,
                'max reconnection attempts': 10,
                'secure': true});

socketio.on('disconnect', function() {
    alert('client socketio disconnect!')
});

The alert shows up if I take down the Node server, but not if I turn off my wireless, or close my laptop and re-open it later. The connection is lost though. Is there a better way to detect disconnect on the client side? This is with the WebSockets transport.

Most helpful comment

Please re-open this ticket, since it's not yet fixed!
I'was looking at the file: https://github.com/socketio/socket.io-client/blob/master/lib/manager.js to figure out if there is any option for that, and I found timeout which is 20 second by default.
But when I've tried to change it to 4 sec, it just didn't work at all.

All 58 comments

Hello,

Did you find a solution to this ?

I'm curious about this too. I'm having trouble getting disconnect events to fire reliably regardless of the situation, but this may be my own fault.

+1

I found a pseudo-solution where I do io.emit('ping') every 30 seconds or so on the front-end. If there has been a disconnect, this ends up triggering an automatic reconnect.

Here is my test, all on Mac using FF 10.0.2, Chrome 17.0.963.79 and Safari 5.1.2 (6534.52.7)
socket.io version = 0.8.7
node.js version = v0.6

The client app listens on all available io events.. connect, connecting, connect_failed, disconnect, reconnecting, reconnect and reconnect_failed and logs out to console

the situation.. i am connect on all browser via websockets

now i turn off wifi =>
FF: no event
Chrome: no event
Safari: event = disconnect

i send a ping from FF and Chrome to provoke a disconnect event =>
FF: no event
Chrome: no event

i turn on wifi again =>
FF: no event
Chrome: no event
Safari: event = reconnect

i send messages between the clients =>
all 3 browsers receive the messages, so the auto-reconnect seems to work, even though the events are not fired in ff and chrome

+1

+1

similar situation here. I've 2 device ipod touch (iOS 4.2.1) + galaxy note. galaxy note can always detect the disconnection of ipod touch while the ipod cannot. Seems safari block incoming data via websocket=[

Still having this issue. I read some posts describing the same issue one year ago. I think i have to work around it myself? ...

+1

+1

+1

Just a small heads up, if you wrap socket.io with primus this issue would be resolved. It uses a combination of ping packets and online/offline events to determine these connections issues.

I was able to fix this issue by adjusting the heartbeat timeout and heartbeat interval settings on the server, which I believe dictates the client side heartbeat. For example, if you'd like the client to trigger a disconnect after it does two heartbeats every 4 seconds and doesn't get a response then you would configure like the following.

io.set('heartbeat timeout', 10) io.set('heartbeat interval', 4)

+1

2 years and no solution at all ? Why Safari is the only browser that could detect the disconnect ? FF and Chrome do not follow the standards ??

+1

Thanks @pwhisenhunt

@pwhisenhunt but for some cases decreasing heartbeat causing another problem.
For example if the network is not stable, socket will disconnected too often because the heartbeat is too short.
I hope this issue will be fixed soon

Yep, that's correct. Thanks for bringing that up! Sorry I must have forgotten that in my previous comment. I'm curious if this is fixed in 1.0?

Please pardon any misspellings, this message was sent from my iPhone.

On Jun 5, 2014, at 9:53 AM, dev0x10 [email protected] wrote:

@pwhisenhunt but for some cases decreasing heartbeat will causing another trouble.
For example if the network is not stable, socket will disconnected too often.
I hope this issue will be fixed soon


Reply to this email directly or view it on GitHub.

Any solution to "disconnect event not getting fired" yet? or am I missing something. As the last solution suggested by @pwhisenhunt is not so reliable.

We are having this same issue on 1.3.6 version of socket.io. The node server in our case is not getting the disconnect event from client when wireless is shut off or laptop is closed.

+1

I am experiencing this same issue, using version 1.4.3. Why is the issue closed?
Thanks

Please re-open this ticket, since it's not yet fixed!
I'was looking at the file: https://github.com/socketio/socket.io-client/blob/master/lib/manager.js to figure out if there is any option for that, and I found timeout which is 20 second by default.
But when I've tried to change it to 4 sec, it just didn't work at all.

Have the same issue. Could be reproduced in chrome without actual internet disconnection. Just open Developer tools -> Network tab -> Choose offline from selectbox.

any news that can work?

Saaaame issue!

Please reopen this, It's not fixed!

Agreed - this is still a problem for me also

hi
i use this solution and fine for me
var io = require('socket.io')(http, {'pingInterval': 2000, 'pingTimeout': 5000});
http://stackoverflow.com/a/31787022/2818627

Hi,
Still not found a good solution for this issue. I turned off my wifi connection set browser mode to offline still disconnect event of socket.io was not raised. was it due to local connection?

Regards,
Suhas

+1

Same issue. This issue needs to be fixed. I have this with Chrome 54.

+1

I have the same issue, event disconnect client-side not detected

  • Did anyone try to debug with localStorage.debug='*'?
  • Does it happen only with websocket transport?
  • Which version of socket.io are you using?

I just tested with the chat example (socket.io 1.7.2 ; chrome ; linux):

image

Websocket gets established:
23:09:37.709 => ping
23:09:37.934 => pong (server response)
23:10:02.938 => ping
23:10:03.169 => pong
I switch off the WIFI
23:10:28.171 => ping
wait for 60s => timeout => disconnect gets fired

25s being the default value for pingInterval, 60s for pingTimeout (ref)

Can anyone confirm/infirm that behaviour?

Also, if the server gets killed, I think that the disconnect event would be fired immediately since the underlying TCP connection is closed.

I can confirm this behavior: disconnect gets fired on both server and client if a network cable is unplugged. However, there is also the case where the client gets disconnected from the server without any events being fired. The below screenshots are from a client that is no longer emitting to the server. No disconnect events have been fired (either on the client or on the server). Note that the client still seems to be receiving pongs from the server:

socketio

socketio2

socketio3

there is also the case where the client gets disconnected from the server without any events being fired

Hmm, I don't understand, how do you know the client gets disconnected then?

Yeah, "disconnected" is probably not the right wording. The issue is that the client stops emitting to the server, although it is still receiving pongs from it. In the last screenshot I posted, socket.emit should have been called in between the ping pong events, but somehow it hasn't.

So, if I understand correctly, the socket is still emitting pings/getting pongs, but socket.emit() gets discarded?

Can you reproduce easily? Is it linked to a given browser?

Ref: https://github.com/socketio/socket.io-client/blob/1.7.2/lib/socket.js#L155

  if (this.connected) {
    this.packet(packet);
  } else {
    this.sendBuffer.push(packet);
  }

But the only way the connected flag is set to false is here:

Socket.prototype.onclose = function (reason) {
  debug('close (%s)', reason);
  this.connected = false;
  this.disconnected = true;
  delete this.id;
  this.emit('disconnect', reason);
};

So it shouldn't be discarded here, since you didn't get the disconnect event. Else, if we look in engine.io-client:

Socket.prototype.sendPacket = function (type, data, options, fn) {
  //...
  if ('closing' === this.readyState || 'closed' === this.readyState) {
    return;
  }

The readyState flag is updated here.

So, if I understand correctly, the socket is still emitting pings/getting pongs, but socket.emit() gets discarded?

Yes, that's it. Will try to find a way to reproduce.

@darrachequesne Turns out my own code was blocking calls to socket.emit. Apologies for having wasted your time. My issue was not the same as the issue of this thread, and certainly not an issue with socket.io.

@creole great, thanks!

To all others who +1'ed, has my comment https://github.com/socketio/socket.io/issues/635#issuecomment-266880918 fixed (or at least explained) your issue?

In any case, maybe a smaller default value for pingTimeout would help? Currently in the worst case one must wait 85s (25000 (pingInterval) + 60000 (pingTimeout)) before getting a disconnect event. How about a pingTimeout of 5s by default (which is what @mbf5923 suggested, btw)?

Changing default setting so much can lead to unexpected behavior in some implementations, but I agree dropping pingTimeoutto around 30s would be more purposeful.

Experiencing this problem as well.. Changing ping interval does not seem reliable enough in production

As long as the bug will not be resolved shortly and a final purpose of what we need is to indicate if user has left, we can extend our socket object by using middleware. Basically, we need to extend socket object with a user and a room the user is currently in and after user got disconnected we can use that properly as we need. Full code is available at: https://github.com/abitlog/retube/blob/master/server/utils/socket.js

  io.use(checkAuth);

  io.on('connection', function (socket) {
    socket.on('join chatroom', function (data) {
      const { id: room } = data;

      socket.join(room);

      io.to(room).emit('join chatroom', {
        user: socket.user,
        counter: socket.adapter.rooms[room].length
      });

      socket.currentChatroomId = room; // extend an object after user joins a room
    });

    socket.on('leave chatroom', function (data) { // this event will not be fired as long as client-side disconnect does not work
      const { id: room } = data;

      io.to(room).emit('leave chatroom', {
        user: socket.user,
        counter: socket.adapter.rooms[room] ? socket.adapter.rooms[room].length - 1 : null
      });

      socket.leave(room);
    });

    socket.on('disconnect', function () {
      const { currentChatroomId } = socket;

      io.to(currentChatroomId).emit('leave chatroom', { // we use currentChatroomId from our socket object to indicate the room user has left
        user: socket.user,
        counter: socket.adapter.rooms[currentChatroomId] ? socket.adapter.rooms[currentChatroomId].length  : null
      });

    });
  });

  function checkAuth(socket, next) {
    if (!socket.request.session.passport) {
      next(new AuthError(404));
      return;
    }

    socket.user = socket.request.session.passport.user;

    next();
  }

@abitlog I may be missing something here, but your use case is not related to the current issue, right?

Please take a look at the disconnecting event.

@darrachequesne I investigated the issue and it seems all events are working fine. It's just in Chrome for mac (didn't notice this in windows version) taking long time (approx 1 min) to trigger disconnect. I tried of course timeout and connect_error as attempt to suplement disconnect as a sibling event but no luck there.

@KamWis which value are you using for pingTimeout/pingInterval?

I just went through all of my connection settings again and I must have had a typo somewhere. When I set pingTimeout and pingInterval on the server, the client appears to respect that even in Chrome. If anyone else is having problems and wants to test this, it helps to set localStorage.debug = 'engine.io-client:socket' in your browser. This will make ping/pong messages show in the console.

I guess the issue can be closed now?

Yep, I believe so. Looks like everything is working as expected.

I can detect disconnect event but it took time at least 8-10 seconds.
would you tell me why it does happen?

@Royalone-mobile please update the pingInterval / pingTimeout values.

I have solved this problem.
it may be a bug of chrome. it‘s still online when we set the network to "offline" mode(谷歌浏览器把网络模式设置为offline的时候websocket好像同样可以通信,网络并没有真正断掉,可以换用两个设备用物理上的断网方式你们自己验证一下是不是这个原因).
the right way is usesocket.on('disconnect',function(){}).
but pingInterval default value is 25000,pingTimeout default value is 60000.it's too long.
so we need to set pingInterval / pingTimeout to a small value.
(其实disconnect事件是可以监听到用户掉线的,只是谷歌浏览器的使用不当埋了第一个坑,然后这个自动掉线检测时间比较长又埋了第二个坑,只需要把pingInterval设置短一点就可以很快检测到用户掉线了)

Was this page helpful?
0 / 5 - 0 ratings