1

假设我有一个服务器程序可以接受来自 10 个(或更多)不同客户端的连接。客户端随机发送数据,服务器接收到这些数据,但可以肯定的是,每次更新都会至少有一个客户端发送数据。服务器不能等待信息到达,因为它还有其他处理要做。除了使用异步套接字之外,我还看到了两个选项:

  1. 使所有套接字不阻塞。在一个循环中,调用recv()每个套接字并允许它失败,WSAEWOULDBLOCK如果没有可用的数据并且如果我碰巧得到了一些数据,那么保留它。

  2. 让套接字保持阻塞状态。将所有套接字添加到 aFD_SET并调用select(). 如果返回值不为零(大多数情况下是这样),则循环遍历所有套接字以找到适当数量的可读套接字,FD_ISSET()并仅调用recv()可读套接字。

第一个选项将创建更多对该recv()函数的调用。第二种方法从编程的角度来看是一个更大的痛苦,因为所有的FD_SETFD_ISSET循环。

首选哪种方法(或另一种方法)?是否避免让recv()非阻塞套接字失败的开销值得调用的麻烦select()

我想我理解这两种方法,并且我都成功地尝试过,但我不知道一种方法是否被认为更好或最优。

4

2 回答 2

2

我建议改用重叠 IO。然后,您可以启动WSARecv(), 并提供一个回调函数,以便在操作完成时调用。更重要的是,因为它只会在您的程序处于警报等待状态时被调用,所以您不需要像在线程应用程序中那样担心锁(假设您在主线程上运行它们)。

但是请注意,您确实需要经常进入这种可提醒的等待状态。如果这是您的 UI 线程,请确保MsgWaitForMultipleObjectsEx()在您的消息循环中使用MWMO_ALERTABLE标志。这将使您的回调有机会运行。在非 UI 线程上,定期调用任何可让您进入警报等待状态的等待函数。

另请注意,模式对话框通常不会进入警报等待状态,因为它们有自己的消息循环,不会调用MsgWaitForMultipleObjectsEx(). 如果您需要在显示对话框时处理网络 IO,请在专用线程上执行所有网络 IO,该线程确实会定期进入警报等待状态。

如果出于某种原因,您不能使用重叠 IO - 一定要使用阻塞select()recv()在无限循环中使用像这样的非阻塞是对 CPU 时间的不可原谅的浪费。但是,请务必将套接字置于非阻塞模式 - 否则,如果一个字节到达并且您尝试读取两个字节,您可能最终会意外阻塞。

您可能还想考虑使用库来抽象出挑剔的细节。例如,libeventboost::asio

于 2009-10-06T15:32:34.560 回答
1

IO 应该完全阻塞每个连接一个线程,在这种情况下,事件循环本质上是一个 OS 调度程序,或者 IO 应该完全非阻塞,在这种情况下,基于 select/waitformultipleobjects 的事件循环将在您的应用程序中

所有中间变体都不是非常可维护且容易出错

当并发连接数量增长并且没有线程上下文切换开销时,完全非阻塞方法的扩展性要好得多,因此在并发连接数不固定的情况下是可取的。与完全阻塞的方法相比,这种方法具有更高的实现复杂性。

对于完全非阻塞 IO,应用程序的核心是基于 select/waitformultipleobjects 的事件循环,所有套接字都处于非阻塞模式,所有读/写通常在事件循环线程内完成(对于最高性能的写可以是首先直接从请求写入的线程尝试)

于 2010-05-10T14:42:30.183 回答