关于 c:如何使用 poll() 检测超时的客户端?

How to detect a timed out client with poll()?

我正在编写一个非分叉服务器,使用 poll() 进行多个同时连接。它工作正常,但我对如何以正确的方式检测超时有问题。

假设我有以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#define POLL_SIZE 512

struct pollfd poll_set[POLL_SIZE];
timeout = 60000; // 60 secs

// setup server_sockfd with socket(), bind(), listen(), ...

poll_set[0].fd = server_sockfd;
poll_set[0].events = POLLIN;
numfds = 1;

while(1) {
   rc = poll(poll_set, numfds, timeout);
   if(rc == 0){
      // handle timeout
   }

   for(fd_index = 0; fd_index < numfds; fd_index++) {
      if(poll_set[fd_index].revents & POLLIN) {
         // accept new connection or handle established connections
      }
   }
}

让我们假设,我有 15 个客户端连接,14 个客户端正在发送和接收数据,但是一个客户端是静默的,没有数据进出,即。只占用服务器上的一个套接字。

现在,问题是 poll() 无法发现这个特定的客户端,因为所有其他 14 个客户端都在提供数据,所以 poll() 说,没关系。

您将如何通过检测此静默客户端并关闭其连接来解决此问题?

目前,我没有更好的方法,然后创建一个 time_t lastseen[POLL_SIZE] 数组,并在从客户端读取数据或将数据发送到客户端时跟踪给定连接的时间戳。

然后我每 60 秒使用一次警报信号,并遍历 lastseen 数组,将它们的时间戳与当前时间戳进行比较,并拆除每个空闲 > 60 秒的连接。

或者一个线程也可以这样做以避免发出信号。你有什么建议来解决这个问题?

(请注意,我尝试了 libevent,它非常好。但是,我不得不放弃它,因为我找不到将 SSL/TLS 添加到已经连接的套接字的支持。想想 STARTTLS)


检测与套接字相关的错误不是轮询的工作。它所做的一切都表明一个或多个套接字是否已准备好进行读写操作。如果任何等待的套接字发生错误,则轮询将该套接字标记为就绪(实际上它由操作系统标记)并在 revents 字段中指示 POLLERR 标志。

超时呢?通常超时不是传输层错误(因此不会被套接字跟踪)。您需要自己跟踪它。例如,您可以记住上次从套接字读取的时间戳(请参阅 clock_gettime(CLOCK_MONOTONIC, ...))并将轮询中??的超时设置为与该套接字相关的所有超时的最小值。超时过期后,您需要检查每个套接字是否过期。

还可以考虑使用 epoll - 在一次轮询中处理大量套接字的速度要快得多。并且对于最近的超时选择,您可以使用堆数据结构。因此,您可以使用 O(log n) 执行时间管理所有套接字。