【问题标题】:How does `select` handle multiple events at the same time?`select` 如何同时处理多个事件?
【发布时间】:2021-03-05 12:22:54
【问题描述】:

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

【问题讨论】:

  • 假设select()实现正确,最好使用非阻塞套接字,不会丢失数据,所有数据都会收到。
  • 编辑注意:严格来说,这既不是 C 也不是 C++ 问题,它是 POSIX 或 BSD 网络问题。但是OP的意思是C++,而且网站是关于“C/C++”的,所以保留原来的语言标签是有道理的。
  • @user4815162342 已记录。错误地标记 c 或 c++ 的问题是如此普遍,以至于我没有意识到。应该多加注意
  • @idclev463035818 不用担心 - 这个问题 在技术上是错误的,所以我的评论更像是对 OP 的宽大处理,而不是对编辑的批评。
  • 请注意,在现代操作系统的新程序中,poll 通常优于 select

标签: c++ sockets unix select tcp


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    select() 是一个级别触发 API,这意味着它回答的问题是“这些文件描述符现在是否可读/可写?”,而不是“这些文件描述符是否变得可读/可写?”。这应该可以回答您的大部分问题:

    但是,如果在第一条消息的很短的时间内,我发送了另一条消息会怎样? [...] 会触发下一个select 吗?

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

    如果我在内循环完成之前发送两条消息会发生什么?

    这取决于消息的长度 - TCP 不是在消息方面起作用,而是在字节流方面起作用。服务器很可能会在单个read() 中读取两条消息。如果没有,套接字将保持可读状态,并会在下一个select() 时立即获取它们。

    我是否会遇到这样的情况:在循环内迭代所有活动套接字时,我得到超过 1 个具有“活动”的套接字 - 即:两个 FD_ISSET(sd, &readfds) 在循环的单次迭代中是否为真?

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

    【讨论】:

      【解决方案3】:

      添加到已经很好的答案:

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

      正如在 cmets 中提到的 @sam-varshavchik 一样,只要正确实现了 select 并且在您通过 select 循环期间没有阻塞数据包缓冲区,就可以了。


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

      【讨论】:

      • 请注意,“以上”取决于答案的排序方式。在我看来,没有上述答案
      • 感谢@idclev463035818 的注解 =)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多