使用多个套接字,是非阻塞还是阻塞,选择更好?

时间:2009-10-06 15:15:04

标签: sockets winsock blocking nonblocking

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

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

  2. 将套接字保留为阻止状态。将所有套接字添加到FD_SET并致电select()。如果返回值非零(大部分时间都是非零),则循环遍历所有套接字以查找具有FD_ISSET()的适当数量的可读套接字,并仅在可读套接字上调用recv()

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

    首选哪种方法(或其他方法)?避免让recv()在非阻塞套接字上失败的开销值得调用select()的麻烦吗?

    我认为我理解这两种方法并且我已经尝试了两种方法,但我不知道一种方法是否被认为是更好或最佳。

2 个答案:

答案 0 :(得分:1)

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

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

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

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

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

答案 1 :(得分:1)

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

所有中间变体都不易维护且容易出错

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

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