因此,由于有关ws.close() 和ws.terminate() 的文档中存在某种遗漏,我认为提供的答案中的解决方案在某些情况下不会优雅地关闭套接字,从而使它们保持在事件循环中。
比较ws包的下两种方法:
初始化关闭握手,向对端发送关闭帧并等待从对端接收关闭帧,然后发送 FIN 数据包以尝试执行干净的套接字关闭。收到答复后,套接字被销毁。但是,有一个 closeTimeout 只会在最坏的情况下破坏套接字,并且它可能会使套接字再保持 30 秒,从而阻止您自定义超时的正常退出:
// ws/lib/WebSocket.js:21
const closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly.
在不关闭帧或fin数据包交换的情况下强制销毁套接字,并且立即执行,没有任何超时。
硬关机
考虑到以上所有因素,“硬着陆”情况如下:
wss.clients.forEach((socket) => {
// Soft close
socket.close();
process.nextTick(() => {
if ([socket.OPEN, socket.CLOSING].includes(socket.readyState)) {
// Socket still hangs, hard close
socket.terminate();
}
});
});
软关机
如果您可以让自己等待一段时间(但不是 30 秒),您可以给您的客户一些时间做出回应:
// First sweep, soft close
wss.clients.forEach((socket) => {
socket.close();
});
setTimeout(() => {
// Second sweep, hard close
// for everyone who's left
wss.clients.forEach((socket) => {
if ([socket.OPEN, socket.CLOSING].includes(socket.readyState)) {
socket.terminate();
}
});
}, 10000);
重要提示:正确执行close()方法会为close事件发出1000关闭代码,而terminate()会用1006(MDN WebSocket Close event)发出异常关闭信号。