Javascript 在forEach循环中调用socket.disconnect不会';不要在所有插座上实际调用disconnect

Javascript 在forEach循环中调用socket.disconnect不会';不要在所有插座上实际调用disconnect,javascript,node.js,sockets,Javascript,Node.js,Sockets,我是javascript世界的新手。最近我正在nodejs中开发一个聊天应用程序。所以我有一个名为gracefulshutdown的方法,如下所示 var gracefulShutdown = function() { logger.info("Received kill signal, shutting down gracefully."); server.close(); logger.info('Disconnecting all the socket.io cli

我是javascript世界的新手。最近我正在nodejs中开发一个聊天应用程序。所以我有一个名为gracefulshutdown的方法,如下所示

var gracefulShutdown = function() {
    logger.info("Received kill signal, shutting down gracefully.");
    server.close();
    logger.info('Disconnecting all the socket.io clients');
    if (Object.keys(io.sockets.sockets).length == 0) process.exit();
    var _map = io.sockets.sockets,
        _socket;
    for (var _k in _map) {
        if (_map.hasOwnProperty(_k)) {
            _socket = _map[_k];
            _socket.disconnect(true);
        }
    }
    ...code here...
    setTimeout(function() {
        logger.error("Could not close connections in time, shutting down");
        process.exit();
    }, 10 * 1000);
}
以下是disconnect listener中发生的情况。removeDisconnectedClient方法只是更新数据库中的一个条目以指示已删除的客户端

socket.on('disconnect',function()){ 移除断开连接的客户端(套接字); });

因此,在本例中,并非所有套接字都触发断开连接事件。它仅从阵列中随机激发了几个套接字。尽管我在队友的帮助下使用setTimeout(fn,0)修复了它


我在网上读到过这篇文章,只知道setTimeout通过将代码添加到事件队列的末尾来延迟代码的执行。我阅读了javascript上下文、调用堆栈和事件循环。但我不能把所有这些都放在这个背景下。我真的不明白这个问题为什么会发生,是怎么发生的。有人能详细解释一下吗。解决或避免它们的最佳方法是什么。

如果没有更多关于
gracefulShutdown
中其余代码的上下文,很难说清楚,但我很惊讶它竟然断开了任何套接字:

_socket = _map[ _k ];
socket.disconnect(true);
似乎您正在将
\u map
中的项目分配给变量
\u socket
,但随后调用
socket
上的
disconnect
,这是一个不同的变量。我猜这是一个打字错误,你想在
\u socket
上调用
disconnect

某些套接字可能由于其他原因而断开连接,而您的循环正在断开某些而不是所有套接字的连接可能只是巧合


从您发布的代码中,我可以看出,
socket
应该是
undefined
,并且您在尝试调用
undefined
上的
disconnect
方法时会出错。我可以假设,在尝试断开所有套接字后,应用程序会退出。套接字通信的本质是异步的,因此如果您在
\u map
中有相当数量的项,那么在进程退出之前,可能不会发送带有
断开连接的所有消息

断开所有套接字的连接后,您可以在超时后调用
exit
,以增加机会。但是,为什么要手动断开连接?连接中断时,远程套接字将自动断开连接

更新
Node.js的Socket.io没有回调来确定发送了带有
disconnect
命令的数据包。至少在v0.9中。我已经调试过了,并得出结论,如果不修改源代码,就不可能捕捉到那个时刻

在文件“socket.io\lib\transports\websocket\hybi-16.js”中,调用方法
write
发送断开连接数据包

WebSocket.prototype.write = function (data) {
...
   this.socket.write(buf, 'binary');
...
}
socket.write
在Node.js核心传输“nodejs-{your Node version}-src\core modules sources\lib\net.js”中定义为

但是,正如您看到的,没有提供此回调,因此socket.io将不知道已发送的数据包

在为websocket调用
disconnect()
的同时,成员
disconnected
被设置为true,并且实际上会广播“disconnect”事件。但是同步的。因此,服务器套接字上的
.on('disconnect'
处理程序不会提供有关数据包是否已发送的重要信息

解决方案
我可以从中得出一个一般性结论:如果确保立即通知所有客户机(而不是等待心跳超时或心跳被禁用)非常关键,那么应该手动实现此逻辑

您可以向客户端发送一条普通消息,这意味着服务器将关闭,并在收到消息后立即调用socket disconnect。同时,服务器将能够接受所有确认

服务器端:

var sockets = [];
for (var _k in _map) {
    if (_map.hasOwnProperty(_k)) {
        sockets.push(_map[_k]);
    }
}
sockets.map(function (socket) {
    socket.emit('shutdown', function () {
        socket.isShutdown = true;
        var all = sockets.every(function (skt) {
            return skt.isShutdown;
        });
        if (all) {
            //wrap in timeout to let current tick finish before quitting
            setTimeout(function () {
                process.exit();
            });
        }
    })
})
客户应该行为简单

socket.on('shutdown', function () {
    socket.disconnect();
});

因此,我们确保每个客户端都已显式断开连接。我们不关心服务器。它将很快关闭。

这里的问题是sockjs和socket.io使用异步“断开连接”方法。即,当您调用disconnect时,它不会立即终止。这只是一个将终止它的承诺。这具有以下效果(假设有3个套接字)

  • for循环抓住第一个套接字
  • 在第一个套接字上调用disconnect方法
  • for循环抓住第二个套接字
  • 在第二个套接字上调用disconnect方法
  • 第一个插座上的断开方法完成
  • for循环抓住第三个套接字
  • 在第三个套接字上调用disconnect方法
  • 程序自杀
  • 请注意,套接字2和3还没有完成,这可能有很多原因


    最后,正如您所说,setTimeout(fn,0)阻止了最后一个调用,但它可能不一致(我没有深入研究太多)。我的意思是,您已将最终终止设置为在所有套接字断开连接之后。setTimeout和setInterval方法本质上更像一个队列。您在队列中的位置由您设置的计时器决定。两个时间间隔各设置为10秒,在这两个时间间隔同步运行时,将导致一个时间间隔在另一个时间间隔之后运行。

    r Socket.io 1.0,库不会向您公开连接的套接字数组。您可以检查io.Socket.sockets.length是否与打开的套接字对象不相等。您最好的选择是向所有要关闭的客户端广播“断开连接”消息,然后打开。客户端上的“断开连接”关闭实际的WebSocket。

    示例代码它看起来像
    io.sockets.sockets
    是一个对象,但至少在
    socket.on('shutdown', function () {
        socket.disconnect();
    });
    
    var a = [1,2,3,4];
    for( var i in a) {
       a.splice(i,1); // remove item from array
       alert(i);
    }
    // alerts 0,1 
    
    var _map = io.sockets.sockets.slice(); // copy of the original