为什么select()后FD_ISSET返回true

问题描述 投票:0回答:2

我是套接字编程的新手,我试图彻底理解它是如何工作的,但现在我真的陷入了

select()

问题是,在我的代码中,在 select 检测到活动并且 fd 保持设置状态后,似乎在下一次迭代中

FD_ISSET
将自动返回 true,就像它会忽略 select 函数一样。这个问题似乎与这个问题相同,但我做了我在那里找到的所有方法,但无济于事:http://compgroups.net/comp.unix.programmer/how-does-fd_isset-return-0-after-返回/55058

我确保在

select()
之后重新初始化 timeval 变量,因为我在 Linux 上,并且我了解这个函数在不同操作系统上的行为有所不同,我还在选择之前使用
FD_ZERO
FD_SET
重新初始化了 fd 集。

我做错了什么?这是代码:

#include <stdio.h>
#include <strings.h>

#include <sys/select.h>
#include <sys/time.h>

int main () {
  struct timeval tv;
  
  tv.tv_sec = 5; // 5 seconds timeout
  tv.tv_usec = 0;
  
  fd_set afds, rfds;
  
  FD_ZERO(&afds);
  FD_SET(0, &afds);

  while (1) {
    rfds = afds;
    select(1, &rfds, NULL, NULL, &tv);
    // linux, reinitialize tv?
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    // so at this point after select runs the first time and detects STDIN activity
    // it will enter an infinite loop printing "fd 0 is set" (why?)
    if (FD_ISSET(0, &rfds)) {
      printf("fd 0 is set\n");
      FD_CLR(0, &rfds);
    } else {
      printf("fd 0 is NOT set\n");
    }
  }
}

问题编辑,因为我是新用户,无法回答这个问题:

事实是我在 select 之前初始化 rfds,当它被分配 afds 的值时,它又总是用 FD_ZERO(&afds); 设置。 FD_SET(0,&afds);这对我来说仍然不起作用。

这是我的理解:

  1. 我将 stdin 文件描述符添加到 afds

  2. 输入while无限循环,rfds = afds(在循环开始时rfds将始终= afds)

  3. 而且,此时FD_ISSET(0, &rfds)将永远是!= 0

  4. select 有 5 秒的超时时间,所以此时如果我在 5 秒过去之前没有输入任何内容,它就会退出,UNSETTING FD_ISSET(0, &rfds) - 这是正确的吗?所以如果没有输入任何内容, select 实际上会取消设置 fd 0。这似乎工作正常

  5. 当我在超时之前输入内容时,问题就出现了。此时FD_ISSET(0, &rfds)返回!= 0,打印fd 0 is set,然后每次循环fd都会被set

好的,这准确吗?我做对了吗?所以实际上 select 不会等待时间过去,因为它实际上检测到 fd 已准备好并退出,设置 fd != 0 ?

这会提出一个进一步的问题:如果我需要服务器每隔一段时间自动向多个客户端发送消息(独立于它从客户端读取的内容),是否可以通过调整 select 和 gettimeofday 来做到这一点上面的代码?

c sockets posix-select
2个回答
16
投票

select()
是电平触发,不是边沿触发。当你向它传递一组文件描述符时,它会回来告诉你哪些文件描述符当前可读/可写/异常,而不仅仅是最近更改状态的文件描述符。

在这种情况下,每次您调用

select()
时,FD 都会被标记为可读,因为当它出现时,您没有采取任何措施使其可读(例如耗尽可用输入)。


6
投票

在 select() 触发后,存储在 fd_set 中的值仍然保留。在应用程序中,select() 可以监视许多套接字。自动清理 fd_set 意味着您永远无法检测到多个套接字需要维护。

您需要在无限循环内执行 FD_ZERO() 和 FD_SET() ,以便在每次调用 select() 之前都干净地初始化 fd_set。

© www.soinside.com 2019 - 2024. All rights reserved.