检测“死套接字”的最佳方法是定期发送应用程序级别的 ping/keepalive 消息。该消息的外观取决于您用于通过套接字进行通信的协议。那么在你向客户端发送 ping/keepalive 消息后的一段时间内,你只需使用计时器或其他方式检查是否收到“ping 响应”即可。
在一个半相关的注释中,看起来您正在使用 JSON 消息进行通信,但您假设每个 data 事件都有一个完整的 JSON 字符串,这是一个错误的假设。尝试使用分隔符(换行符在这种情况下很常见,它使调试通信更易于阅读)。
这里有一个简单的例子来说明如何做到这一点:
var PING_TIMEOUT = 5000, // how long to wait for client to respond
WAIT_TIMEOUT = 5000; // duration of "silence" from client until a ping is sent
var server = net.createServer(function(stream) {
stream.setEncoding('utf8');
var buffer = '',
pingTimeout,
waitTimeout;
function send(obj) {
stream.write(JSON.stringify(obj) + '\n');
}
stream.on('data', function(data) {
// stop our timers if we've gotten any kind of data
// from the client, whether it's a ping response or
// not, we know their connection is still good.
clearTimeout(waitTimeout);
clearTimeout(pingTimeout);
buffer += data;
var idx;
// because `data` can be a chunk of any size, we could
// have multiple messages in our buffer, so we check
// for that here ...
while (~(idx = buffer.indexOf('\n'))) {
try {
var comm = JSON.parse(buffer.substring(0, idx));
// join request getting from client
if (comm.action === "Join_Request" && comm.gameId === "game1") {
send({ message: 'WaitRoom' });
}
} catch (ex) {
// some error occurred, probably from trying to parse invalid JSON
}
// update our buffer
buffer = buffer.substring(idx + 1);
}
// we wait for more data, if we don't see anything in
// WAIT_TIMEOUT milliseconds, we send a ping message
waitTimeout = setTimeout(function() {
send({ message: 'Ping' });
// we sent a ping, now we wait for a ping response
pingTimeout = setTimeout(function() {
// if we've gotten here, we are assuming the
// connection is dead because the client did not
// at least respond to our ping message
stream.destroy(); // or stream.end();
}, PING_TIMEOUT);
}, WAIT_TIMEOUT);
});
// other event handlers and logic ...
});
您也可以只有一个间隔而不是两个计时器来检查“最后收到的数据”时间戳与当前时间戳,如果它超过一定时间长度并且我们最近发送了一条 ping 消息,那么您假设套接字/连接已死。您也可以改为发送多个 ping 消息,如果在发送 n ping 消息之后没有收到响应,则在此时关闭连接(这基本上是 OpenSSH 所做的)。
有很多方法可以解决这个问题。不过,您也可以考虑在客户端做同样的事情,这样您就知道服务器也没有失去连接。