`select` 如何同时处理多个事件?

How does `select` handle multiple events at the same time?

我正在尝试理解以下内容 code。如果我有 50 个连接到该服务器并且我通过这些套接字之一发送数据,带有内部循环的 select 块将捕获我发送的内容并将其回显。但是,如果在第一条消息的很短时间内,我发送了另一条消息,会发生什么情况?如此之快,以至于内部循环(在 select 之后 - 遍历所有活动客户端套接字的循环)没有完成。这些数据会被丢弃吗?下一个 select 会被触发吗?如果我在内部循环完成之前发送两条消息会怎样?我是否会遇到在循环内迭代所有活动套接字我得到超过 1 个具有“activity”的场景 - 即:两个 FD_ISSET(sd, &readfds) 可以在循环的单个迭代中为真吗?

select() 级别触发的 API,这意味着它回答了“这些文件描述符中的任何一个 readable/writable现在?”,而不是“这些文件描述符变成 readable/writable 了吗?”。这应该可以回答您的大部分问题:

But what happens if within a very short time-frame of the first message, I send another one? [...] Will it be what the next select will be triggered with?

这将是下一个 select() 将被触发的内容。

What happens if I send two messages before the inner loop finishes ?

这取决于消息的长度 - TCP 不适用于消息,而是字节流。服务器可能会在单个 read() 中读取两条消息。如果没有,套接字将保持可读性,并会在下一个 select().

时立即拾取它们

Will I ever face the scenario where inside the loop iterating over all the active sockets I get more than 1 that has "activity" - i.e.: can two FD_ISSET(sd, &readfds) be true within a single iteration of the loop ?

是的,如果两个客户端同时发送数据(当你在select()之外),select()会报告两个可读文件描述符。

是的,可以在单次迭代中准备好读取多个描述符。 select() 的 return 值是准备好的描述符的数量,它可以大于 1。当你遍历描述符时,你应该在 FD_ISSET(sd, &readfds) 为真时增加一个计数器, 并继续直到计数器达到这个数字。

但即使只处理一个描述符,也不会丢掉任何东西。 select() 不会由更改触发,只要任何描述符准备好读取(或写入,如果您还使用 writefds),它就会触发 return。如果描述符已准备好读取,但您没有从中读取,那么下次您调用 select() 时它仍然可以读取,因此它会立即 return。

但是,如果您只处理在循环中找到的第一个描述符,则如果较早的描述符始终准备好读取,则较晚的描述符可能会“饿死”,而您永远不会处理较晚的描述符。所以通常最好始终处理所有就绪的描述符。

添加到已经很好的答案中:

本例中的 select 函数不是直接从线路上抓取数据包,它实际上是进入数据包缓冲区,通常是 NIC 的一部分,以抓取 packets/frames 可供阅读。数据包缓冲区通常是一个环形缓冲区:它具有固定大小,新数据包从“顶部”进入,当缓冲区满时,最旧的数据包从“底部”丢弃。

正如@sam-varshavchik 在评论中提到的那样,只要 select 正确实施并且数据包缓冲区在您经历 select 循环期间不会堵塞,你会没事的。


Here's 一篇关于如何为套接字实现数据包环形缓冲区的有趣文章。