`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 一篇关于如何为套接字实现数据包环形缓冲区的有趣文章。
我正在尝试理解以下内容 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 一篇关于如何为套接字实现数据包环形缓冲区的有趣文章。