【问题标题】:socket.io: Disconnect event - 'transport close', 'client namespace disconnect', 'transport error' and 'forced close'socket.io:断开连接事件 - “传输关闭”、“客户端命名空间断开连接”、“传输错误”和“强制关闭”
【发布时间】:2015-05-18 09:15:12
【问题描述】:

使用 socket.io v1.2.1(仅使用“轮询”传输),有时我的客户会遇到断开连接。

大约 50% 的时间我在断开连接事件回调函数中收到 ping timeout,这是合理的。

其他时候,我会收到 transport closeclient namespace disconnecttransport errorforced close。我在文档中没有找到任何关于这些断开连接原因的参考,并且无法从代码中真正理解它们的含义。

我想确保以最佳方式处理每次断开连接(并可能阻止它们)。

也许有人可以解释一下这些原因。

【问题讨论】:

    标签: javascript node.js html socket.io


    【解决方案1】:

    我遇到过类似的情况,从服务器到客户端的更新过多(大约 2000 次在几秒钟内)似乎会导致连接被强制关闭。不经常发送更新可以解决问题。

    我还没有机会找出这是由 socket.io 本身引起的,还是由某些防火墙/服务器机制引起的。可能是因为充斥服务器和客户端之间的连接会扰乱心跳机制,尽管客户端日志没有显示。

    虽然还没有找到确切的原因,但也许这可以为一些人指明正确的方向以找到解决方案。

    【讨论】:

    • 对我们来说,原因似乎是事件循环在继续之前被“卡住”在特定阶段,因此需要很长时间才能回到 socket.io 的东西,以至于 socket.io 心跳超时并且必须重新连接。这在snyk.io/blog/… 中有描述。我们的解决方案是分批处理类似类型的“阶段动作”,以防止在一个阶段中将事件循环保持太长时间。它解决了我们的超时问题。也许它可以帮助某人遇到这个话题的具体情况。
    【解决方案2】:

    在服务器端试试这个代码

    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});
    

    这里pingInterval 很重要,保持较低,我尝试了各种值,发现 25 秒可以让套接字在超时之前保持 ping 正常。

    主要问题是如果在 60 秒内没有 ping/pong,那么它将断开连接并尝试自动重新连接。此外,我在服务器端发现 pingTimeout 并且超时和客户端无法帮助在 60 秒内断开套接字。这是由于 chrome 最新版本 83 而发生的。

    【讨论】:

    【解决方案3】:

    没有文档,这或多或少是我可以从代码中解释的:

    Forced close - 套接字处于关闭状态

    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();
    };
    

    关闭的交通工具(这里没有理由)

    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");
      }
    

    我们得到一个客户端断开数据包,所以我们将套接字状态更改为“关闭”

    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');
    };
    

    运输关闭的原因之一

    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);
      }
    };
    

    似乎他们从各处向套接字抛出错误,因此找到原因的唯一方法是阅读错误描述(信息不要太多)或查看他们所有的库以找出导致错误的原因。

    PD:有很多错误。

    【讨论】:

      【解决方案4】:

      很遗憾,这可能会发生。有时,因为我遇到了不幸,这是由于我、服务器和其他客户端之间的防火墙。

      对于 ping 超时,您可以尝试增加服务器端的 ping 间隔

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

      【讨论】:

      • 在我看来,增加pingInterval 值只是一种解决方法,而不是实际的解决方案。这将导致真正失去连接的客户端看起来仍然连接。
      猜你喜欢
      • 2018-12-08
      • 1970-01-01
      • 2017-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-26
      • 2015-01-15
      • 1970-01-01
      相关资源
      最近更新 更多