5
while (xxx) {
    timeout.tv_sec=TIMEOUT;
    timeout.tv_usec=0;
    FD_ZERO(&set); 
    FD_SET(sd,&set);

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
    xxxxx
}

工作正常,但是

FD_ZERO(&set); 
FD_SET(sd,&set);

while (xxx) {
    timeout.tv_sec=TIMEOUT;
    timeout.tv_usec=0;

    switch (select(FD_SETSIZE,&set,NULL,NULL,&timeout))
    xxxxx
}

没有。它第一次工作,但下一次运行 while 循环时,即使 sd 套接字接收到数据,它也会超时。在我看来,每次都必须清空和填充集是浪费资源。

任何人都有一个很好的解释为什么会这样,甚至更好,也许是一个如何避免它的建议?

4

3 回答 3

12

select 修改它的参数。您确实必须每次都重新初始化它。

如果您担心开销,在内核中处理完整 FD_SET 的成本比 FD_ZERO 的成本要高一些。您只想传递最大 fd,而不是 FD_SETSZIZE,以最小化内核处理。在您的示例中:

switch (select((sd + 1),&set,NULL,NULL,&timeout))

对于具有多个 fd 的更复杂的情况,您通常最终会维护一个 max 变量:

FD_SET(sd,&set);
if (sd > max) max = sd;
... repeat many times...

switch (select((max + 1),&set,NULL,NULL,&timeout))


如果您将拥有大量文件描述符并且担心使用它们的开销,那么您应该查看一些 select() 的替代方法。您没有提及您正在使用的操作系统,但对于类 Unix 操作系统,有一些:

  • 对于 Linux,epoll()
  • 对于 FreeBSD/NetBSD/OpenBSD/MacOS X,kqueue()
  • 对于 Solaris,/dev/poll

API 是不同的,但它们本质上都是一个有状态的内核接口,用于维护一组活动文件描述。一旦将 fd 添加到集合中,您将收到有关该 fd 上的事件的通知,而无需再次继续传递它。

于 2008-11-13T04:03:04.067 回答
7

阅读选择手册页。返回的集合只是准备好使用的文件描述符。您应该使用 FD_ISSET 检查每一个是否已设置。

始终在使用 fd_set 之前对其进行初始化。

于 2008-11-12T23:28:22.220 回答
0

这就是选择的工作方式。如果您有多个插槽,它的效果最好,并且更有意义。这就是重点:您在许多套接字中进行选择。如果您想从一个套接字读取,只需读取或接收它。

于 2008-11-12T23:29:49.687 回答