Gilad Artzi
Gilad Artzi

Reputation: 3084

socket.io: Disconnect event - 'transport close', 'client namespace disconnect', 'transport error' and 'forced close'

Using socket.io v1.2.1 (only using the "polling" transport), sometimes my clients experience disconnections.

About 50% of the time I get ping timeout on my disconnect event callback function, which is reasonable.

Other times, I get transport close, client namespace disconnect, transport error and forced close. I did not find any reference to those disconnection reasons in the documentation, and was not able to really understand their meaning from the code.

I want to make sure I handle each disconnection the best way (and maybe prevent them).

Maybe someone can shed a little light about these reasons.

Upvotes: 19

Views: 39791

Answers (4)

Centillion
Centillion

Reputation: 381

I had a similar situation where too many updates from server to client (around 2000 in a matter of seconds) seemed to cause a forced close of the connection. Sending updates less often solved the problem.

I haven't had a chance yet to find out whether this was caused by socket.io itself, or some firewall/server mechanism. Maybe because flooding the connection between server and client messes up the heartbeat mechanism, although client-side log doesn't show that.

Although the exact cause is not found yet, maybe this can point some people into the right direction to find a solution.

Upvotes: 0

Gopal Kohli
Gopal Kohli

Reputation: 151

Try this code at server side

var fs = require('fs');
var pkey = fs.readFileSync('/etc/ssl/private/ssl.key'); //Replace the path of your SSL key
var pcert = fs.readFileSync('/etc/ssl/certs/ssl.crt');//Replace the path of your SSL cert
var options = {
  key: pkey,
  cert: pcert
};

var app = require('https').createServer(options);

var io = require('socket.io')(app, {'pingTimeout': 180000, 'pingInterval': 25000});

Here pingInterval is important, Keep it low, I tried various values and found 25 sec is good to keep socket keep pinging before it get timeout.

The main issue is there if within 60 sec there is no ping/pong, then it will disconnect and try to auto reconnect. Also, I found pingTimeout at server side and timeout and client side not able to help to disconnect the socket in 60 sec. It's happening due to chrome latest version 83.

Upvotes: 1

Jairo
Jairo

Reputation: 350

There is no documentation, this is more or less what i can interpret from the code:

Forced close - The socket is in closing state

Forced close - https://github.com/socketio/engine.io/blob/master/lib/socket.js

function onPacket(packet){
    if ('ping' == packet.type && 'probe' == packet.data) {
      transport.send([{ type: 'pong', data: 'probe' }]);
      self.emit('upgrading', transport);
      clearInterval(self.checkIntervalTimer);
      self.checkIntervalTimer = setInterval(check, 100);
    } else if ('upgrade' == packet.type && self.readyState != 'closed') {
      debug('got upgrade packet - upgrading');
      cleanup();
      self.upgraded = true;
      self.clearTransport();
      self.setTransport(transport);
      self.emit('upgrade', transport);
      self.setPingTimeout();
      self.flush();
      if (self.readyState == 'closing') {
        transport.close(function () {
          self.onClose('forced close');
        });
      }
    } else {
      cleanup();
      transport.close();
    }
  }


Socket.prototype.close = function () {
  if ('open' != this.readyState) return;

  this.readyState = 'closing';

  if (this.writeBuffer.length) {
    this.once('drain', this.closeTransport.bind(this));
    return;
  }

  this.closeTransport();
};

The transport where closed (no reason here)

Transport close - https://github.com/socketio/engine.io/blob/master/lib/socket.js

 function cleanup() {
    self.upgrading = false;

    clearInterval(self.checkIntervalTimer);
    self.checkIntervalTimer = null;

    clearTimeout(self.upgradeTimeoutTimer);
    self.upgradeTimeoutTimer = null;

    transport.removeListener('packet', onPacket);
    transport.removeListener('close', onTransportClose);
    transport.removeListener('error', onError);
    self.removeListener('close', onClose);
  }


  function onTransportClose(){
    onError("transport closed");
  }

We got a client disconnect packet, so we change socket state to 'closing'

Client namespace disconnect - https://github.com/socketio/socket.io/blob/master/lib/socket.js

Socket.prototype.onpacket = function(packet){
  debug('got packet %j', packet);
  switch (packet.type) {
    case parser.EVENT:
      this.onevent(packet);
      break;

    case parser.BINARY_EVENT:
      this.onevent(packet);
      break;

    case parser.ACK:
      this.onack(packet);
      break;

    case parser.BINARY_ACK:
      this.onack(packet);
      break;

    case parser.DISCONNECT:
      this.ondisconnect();
      break;

    case parser.ERROR:
      this.emit('error', packet.data);
  }
};


Socket.prototype.ondisconnect = function(){
  debug('got disconnect packet');
  this.onclose('client namespace disconnect');
};

One of the reasons of transport close

Transport error - https://github.com/socketio/engine.io/blob/master/lib/socket.js

/**
 * Called upon transport error.
 *
 * @param {Error} error object
 * @api private
 */

Socket.prototype.onError = function (err) {
  debug('transport error');
  this.onClose('transport error', err);
};

https://github.com/socketio/engine.io/blob/master/lib/transport.js

/**
 * Called with a transport error.
 *
 * @param {String} message error
 * @param {Object} error description
 * @api private
 */

Transport.prototype.onError = function (msg, desc) {
  if (this.listeners('error').length) {
    var err = new Error(msg);
    err.type = 'TransportError';
    err.description = desc;
    this.emit('error', err);
  } else {
    debug('ignored transport error %s (%s)', msg, desc);
  }
};

It seems like they throw errors to sockets from everywhere, so the only way of find the cause is by reading the error description (not too much information) or looking all their libraries to find what is causing the error.

PD: there are a lot of errors.

Upvotes: 6

kaytrance
kaytrance

Reputation: 2757

Unfortunately this may happen. Sometimes, as I had misfortune to deal with, it was due to firewall somewhere in between me, server and other client.

For ping timeouts you may try to increase ping interval on server side

io = require( 'socket.io' )( httpServer, { pingInterval: 60000 } );

Upvotes: -2

Related Questions