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 套接字接收到数据,它也会超时。在我看来,每次都必须清空并填充集合是浪费资源。
任何人都可以很好地解释为什么会这样,甚至更好,也许可以建议如何避免它?
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 操作系统,有一些:
API 有所不同,但它们本质上都是一个有状态的内核接口,用于维护一组活动文件描述。一旦将一个 fd 添加到集合中,您将收到该 fd 上的事件通知,而无需不断地再次传递它。
阅读选择的手册页。返回的集合只是准备使用的文件描述符。您应该使用 FD_ISSET 检查每一项是否已设置。
始终在使用 fd_set 之前对其进行初始化。
这就是 select 的工作方式。如果您有多个插座,那么它的效果最好,而且更有意义。这就是要点:您正在许多套接字中进行选择。如果您想从一个套接字读取数据,只需读取或接收它即可。