【问题标题】:Non-blocking socket with poll带轮询的非阻塞套接字
【发布时间】:2011-03-22 14:34:33
【问题描述】:

几天前,我不得不调查一个问题,即我的应用程序在(显然)处于空闲状态时显示异常高的 CPU 使用率。我将问题追踪到一个循环,该循环旨在阻止recvfrom 调用,而套接字已设置为O_NONBLOCK-ing,从而导致自旋锁。有两种解决问题的方法:将套接字设置为阻塞或使用pollselect 轮询套接字上的可用数据。我选择了前者,因为它更简单。但我想知道为什么有人会创建一个非阻塞套接字然后单独轮询它。阻塞套接字不做同样的事情吗?使用非阻塞套接字和轮询组合的用例是什么?在一般情况下它有什么优势吗?

【问题讨论】:

    标签: c sockets posix blocking


    【解决方案1】:

    poll()select() 与非阻塞文件描述符一起使用会给您带来两个好处:

    • 您可以设置阻塞的超时时间;
    • 您可以等待任何文件描述符变得可用。

    如果您只有一个文件描述符(套接字)要等待,并且您不介意无限期地等待它,那么可以;你可以只使用阻塞调用。

    第二个优势确实是select() 和朋友的杀手级用例。这意味着您可以处理多个套接字连接,以及标准输入和标准输出以及可能的文件 I/O,所有这些都使用单个控制线程。

    【讨论】:

      【解决方案2】:

      我在这里发帖,因为虽然这个问题很老。它以某种方式出现在我的谷歌搜索中,并且肯定没有得到正确的回答。

      接受的答案仅强调使用非阻塞套接字的两个优点,但并未真正详细说明或回答实际问题。

      • 注意:不幸的是,大多数在线“教程”或代码 sn-ps 仅包含阻塞套接字代码,因此关于非阻塞套接字的知识传播较少。

      至于你什么时候使用一个和另一个相比......通常阻塞套接字只用于在线代码sn-ps。在所有(良好的)生产应用程序中都使用了非阻塞套接字。我不是无知的,如果您知道使用阻塞套接字的实现(并且确保这很可能与线程结合使用) - 或者让我们更具体地在单个线程中使用阻塞套接字 - 请让我知道。

      现在我可以给你一个非常容易理解的例子,还有很多其他的例子。让我们以游戏服务器为例。无论玩家是否提供输入(鼠标/键盘)来改变游戏状态,游戏都会按节拍进行,游戏状态会定期进行。现在,当套接字在多人游戏中发挥作用时——如果你要使用阻塞套接字,除非玩家发送更新,否则游戏状态将不会前进——因此,如果他们有互联网问题,游戏状态将永远不会持续更新并将更改传播给所有玩家.你会有一个相当不稳定的体验。

      现在使用非阻塞套接字,您可以在单线程上运行游戏服务器,更新游戏状态和套接字,并且有一个......比如说 50 毫秒的超时间隔 - 并且套接字数据只能从在用户实际发送内容时连接用户,然后输入服务器模拟、处理并输入游戏状态计算以进行下一次滴答。

      【讨论】:

      • 不要selectpoll 在您执行该操作时通知您特定操作不会阻塞 来缓解阻塞问题?这些优秀的生产应用程序是否仍然禁用阻塞?
      • @TheBuzzSaw 我不确定我是否理解这个问题。我们在这里排除了单个套接字的使用,因此您有一组每次调用都使用的文件描述符,它不是 select,而是实际的 recv 调用阻塞,等待接收数据。
      • 我是说当一个程序使用select 来复用十几个套接字时,通常没有必要将这些套接字置于非阻塞模式,因为对select 的调用决定了哪个动作不会阻塞。如果select 返回并告诉我套接字45 不会在读取时阻塞,我可以调用recv 而不必担心阻塞,对吧?
      • @TheBuzzSaw 我没有对此进行测试,但如果你的 fd 集中有阻塞套接字,那么 recv 调用应该阻塞。混合阻塞和非阻塞套接字是没有意义的。你有这个用例吗?另外不要忘记处理原始套接字会带来一系列问题。在您的 select 和 recv 调用之间,套接字可能会失效 - 虽然很少见,但确实会发生,现在取决于实现,您对无效套接字的 recv 调用是否可能永远阻塞。除了一些非常特殊的场景外,根本没有理由使用阻塞套接字。
      【解决方案3】:

      导致自旋锁。

      这种情况通常称为紧密循环

      有两种解决问题的方法:将套接字设置为阻塞或使用 poll 或 select 轮询套接字上的可用数据。我选择了前者,因为它更简单。

      您确定其他代码部分尚未使用poll()(或select())并希望套接字处于非阻塞模式?

      否则的话是的,切换到阻塞模式是最简单的解决方案。

      最佳 向后兼容的解决方案是在调用 recvfrom() 以使用 poll() 等待套接字变得可读之前。这样可以确保代码的其他部分可以像以前一样精确地工作。

      但我想知道为什么有人会创建一个非阻塞套接字然后单独轮询它。阻塞套接字不做同样的事情吗?

      对于recvfrom(),我不知道有什么大的区别。

      使用非阻塞套接字和轮询组合的用例有哪些?一般情况下它有什么优势吗?

      可能是一个简单的编码错误。或者有人可能认为在紧密循环中进行回收会以某种方式提高性能。

      【讨论】:

        【解决方案4】:

        将套接字设置为nonblocking 总是更好,因为即使是阻塞套接字有时也会变为就绪状态(当数据到达但校验和错误并被丢弃时) - 即使没有数据可读取。所以把它设为nonblocking,等待通过poll的数据可用性然后读取。我认为这是主要优势。

        【讨论】:

          猜你喜欢
          • 2010-10-31
          • 2018-09-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-10-15
          • 1970-01-01
          • 1970-01-01
          • 2013-06-03
          相关资源
          最近更新 更多