Javascript 在forEach循环中调用socket.disconnect不会';不要在所有插座上实际调用disconnect
我是javascript世界的新手。最近我正在nodejs中开发一个聊天应用程序。所以我有一个名为gracefulshutdown的方法,如下所示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
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