【问题标题】:Why is select not signalling a file descriptor with buffered data?为什么选择不使用缓冲数据向文件描述符发出信号?
【发布时间】:2019-02-04 10:21:49
【问题描述】:

我有一个服务器程序,它使用 select 和 10 秒超时来等待多个非阻塞客户端连接上的活动。每次select 调用表明有输入要读取时,服务器将读取并处理多达 1K 的数据。如果有要发送的响应,它将发送它。然后它会回到select

fcntl(clientFd, F_SETFL, fcntl(clientFd, F_GETFL, 0) | O_NONBLOCK);
while (true) {
  FD_ZERO(&readfds);
  FD_SET(clientFd, &readfds);
  timeout = (struct timeval){.tv_sec = 10};
  select(clientFd + 1, &readfds, NULL, NULL, &timeout);
  if (FD_ISSET(clientFd, &readfds)) {
    uint8_t recv_buffer[1024];
    fread(recv_buffer, 1, 1024, clientFile);
    // process & maybe respond / fflush
  }
}

客户端消息的范围从小型(10 或 100 字节)消息到大型(3-10K 消息)。客户端将在挂断之前等待最多 30 秒的响应。当他们挂断时,他们会发送一个 10 字节的小挂断消息。

一个场景正在上演,打破了我对select 应该如何工作的理解。客户端发送的消息小于读取缓冲区,因此服务器读取整个内容并做出响应。然后客户端发送一条 3K 消息。服务器读取第一个 1K,然后随后的select 调用超时。我希望select 立即返回并表示如果内核为该文件描述符缓冲了数据,则有可用的数据。客户端超时后,发送挂断消息。当挂断消息到达时,服务器突然能够select该文件描述符,它读取客户端较大消息的第二和第三块,然后是客户端较小的消息。

我非常确定这些事件的发生时间,因为 (1) 涉及的长时间超时,(2) 确认 3K 消息作为单个 TCP 段到达的对话的 tcpdump。

使用pipe 的简单演示程序不会出现这种行为,另一个使用TCP 套接字的简单演示程序也不会出现这种情况。所以我必须在服务器程序中做一些愚蠢的事情。我应该检查什么?

我正在检查:

  • 客户端的read FD在select之前的read FD set中
  • 客户端的read FD是否在select后的read FD set中(不在)
  • 读取数据的大小(如果至少有这么多可用,则始终为 1K)
  • fread 的第二次调用是否会阻塞(它不会。我在调试器中尝试过,我还使用大小为 4K 的 recv_buffer 进行了不同的运行,它读取了整个 3K 消息)
  • 通过tcpdump 的数据包分段(无)

【问题讨论】:

  • fread 是一个缓冲 I/O 函数。你已经放弃了所有的控制权。您不知道从底层文件描述符中真正读取了多少字节。您可以尝试使用setvbuf 使您的文件无缓冲。
  • 你应该使用 read() 并实际检查 select 的返回值。
  • @n.m.我没有考虑过fread 会在libc 内部缓冲而不使用内核的缓冲区。
  • @Huckle 哦,我的错。我将删除评论。我倾向于在大多数时间使用民意调查而不是选择并误读手册。
  • 然后你必须问自己freadread 有什么不同,以及为什么它首先存在于上帝的绿色地球上。

标签: c select linux-kernel posix nonblocking


【解决方案1】:

张贴什么@n.m。说。如果他们发表评论作为答案,将删除:

read 是一个缓冲 I/O 函数。你已经放弃了所有的控制权。您不知道真正从底层文件描述符中读取了多少字节。您可以尝试使用setvbuf 使您的文件无缓冲。 - n.m.

【讨论】:

    猜你喜欢
    • 2014-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-13
    • 2020-11-26
    • 2021-02-20
    • 1970-01-01
    相关资源
    最近更新 更多