处理套接字服务器中的超时

本文关键字:超时 服务器 套接字 处理 | 更新日期: 2023-09-27 18:09:55

我有一个异步套接字服务器,它包含所有连接的客户机的线程安全集合。如果客户端在一段时间内没有活动(即超时),服务器应用程序应该断开与客户端的连接。有人能提出最好的方法来有效地跟踪每个连接的客户端超时,并在客户端超时时断开连接吗?此套接字服务器必须具有非常高的性能,并且在任何给定时间都可以连接数百个客户端。

一种解决方案是让每个客户端与最后一个活动时间戳相关联,并让计时器周期性地轮询集合,以根据该时间戳查看哪个连接超时,并断开连接。然而,这个解决方案对我来说不是很好,因为这意味着计时器线程必须在轮询时锁定集合(防止任何其他连接/断开),以检查每个连接的客户端并在超时时断开连接。

任何建议/想法都将非常感谢。谢谢。

处理套接字服务器中的超时

如果这是一个新项目,或者如果你愿意对项目进行重大重构,请查看响应式扩展。Rx为异步调用中的超时提供了一个优雅的解决方案:

var getBytes = Observable.FromAsyncPattern<byte[], int, int, int>(_readStream.BeginRead, _readStream.EndRead);
getBytes(buffer, 0, buffer.Length)
     .Timeout(timeout);

注意,上面的代码只是为了演示如何在Rx中超时,在实际项目中会更复杂。

性能方面,我不能说,除非你概述你的具体用例。但我看过一个演讲,他们在一个复杂的数据驱动平台(可能像你的需求)中使用Rx,他们说他们的软件能够在不到30毫秒的时间内做出决定。

代码方面,我发现使用Rx使我的代码看起来更优雅,更简洁。

在许多情况下,与枚举其上下文集合的性能相比,连接的客户机数量较少,您的解决方案非常适合。如果您允许在超时时间内有一些回旋余地(即可以在不活动的55到65秒之间断开客户端),我只是经常运行清除例程。

另一种对我很有效的方法是使用活动令牌队列。让我们使用c#:

class token {
    ClientContext theClient; // this is the client we've observed activity on
    DateTime  theTime;       // this is the time of observed activity
};
class ClientContext {
    // ... what you need to know about the client
    DateTime lastActivity;   // the time the last activity happened on this client
}

每次在特定客户端上发生活动时,都会生成令牌并将其推入FIFO队列,并在ClientContext中更新lastActivity

有另一个线程运行以下循环:

  • 从FIFO队列中提取最老的令牌;
  • 检查令牌中的theTime是否匹配theClient.lastActivity;
  • 如果是,关闭客户端;
  • 查看队列中下一个最老的令牌,计算在需要关闭之前还剩下多少时间;
  • Thread.Sleep(<this time>);

这种方法的代价很小,但不变,即O(1)开销。人们可以想出更快的最佳情况解决方案,但对我来说,似乎很难想出一个更快的最坏情况的性能。

我认为在连接线程内控制超时形式更容易。所以你可以这样写:

// accept socket and open stream
stream.ReadTimeout = 10000; // 10 seconds
while (true)
{
    int bytes = stream.Read(buffer, 0, length)
    // process the data
}

stream.Read()调用将返回数据(这意味着客户端是活的)或抛出IO异常(异常断开)或返回0(客户端关闭套接字)。