Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 818
Gilad Artzi Avatar asked Mar 16 '15 09:03

Gilad Artzi


2 Answers

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.

like image 64
Jairo Avatar answered Sep 29 '22 10:09

Jairo


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.

like image 31
Gopal Kohli Avatar answered Sep 29 '22 09:09

Gopal Kohli