假设我有一个服务器程序可以接受来自10个(或更多)不同客户端的连接。客户端随机发送由服务器接收的数据,但可以肯定的是,每次更新时至少有一个客户端将发送数据。服务器无法等待信息到达,因为它还有其他处理要做。除了使用异步套接字外,我还看到两个选项:
使所有套接字无阻塞。在一个循环中,在每个套接字上调用recv()
并允许它在WSAEWOULDBLOCK
时失败,如果没有可用的数据,并且如果我碰巧得到一些数据,那么保留它。
将套接字保留为阻止状态。将所有套接字添加到FD_SET
并致电select()
。如果返回值非零(大部分时间都是非零),则循环遍历所有套接字以查找具有FD_ISSET()
的适当数量的可读套接字,并仅在可读套接字上调用recv()
第一个选项会创建更多对recv()
函数的调用。从编程的角度来看,第二种方法是一个更大的痛苦,因为所有FD_SET
和FD_ISSET
循环。
首选哪种方法(或其他方法)?避免让recv()
在非阻塞套接字上失败的开销值得调用select()
的麻烦吗?
我认为我理解这两种方法并且我已经尝试了两种方法,但我不知道一种方法是否被认为是更好或最佳。
答案 0 :(得分:1)
我建议改用overlapped IO。然后,您可以启动WSARecv()
,并提供在操作完成时调用的回调函数。更重要的是,因为它只会在你的程序处于可警告的等待状态时被调用,所以你不必像在线程应用程序中那样担心锁(假设你在主线程上运行它们)。
但请注意,您执行需要经常进入这种可警告的等待状态。如果这是您的UI线程,请确保在消息循环中使用MWMO_ALERTABLE
并使用MsgWaitForMultipleObjectsEx()
标志。这将使您的回调有机会运行。在非UI线程上,定期调用任何使您进入可警告等待状态的MsgWaitForMultipleObjectsEx()
。
另请注意,模态对话框通常不会进入可警告的等待状态,因为它们有自己的消息循环,而不会调用select()
。如果在显示对话框时需要处理网络IO,请在专用线程上执行所有网络IO,该线程会定期进入可警告的等待状态。
如果出于某种原因,您无法使用重叠IO - 请务必使用阻止recv()
。在无限循环中使用非阻塞{{1}}是不可原谅的浪费CPU时间。但是,请将套接字置于非阻塞模式 - 否则,如果一个字节到达并且您尝试读取两个,则可能最终意外阻塞。
您可能还想考虑使用库来抽象出挑剔的细节。例如,wait functions或libevent。
答案 1 :(得分:1)
所有中间变体都不易维护且容易出错
当并发连接数量增长且没有线程上下文切换开销时,完全非阻塞方法可以更好地扩展,因此在不修复并发连接数的情况下,这是一种优选方法。与完全阻止一种方法相比,这种方法具有更高的实现复杂性。
对于完全无阻塞的IO,应用程序的核心是基于select / waitformultipleobjects的事件循环,所有套接字都处于非阻塞模式,所有读取/写入通常都是在事件循环线程内完成的(为了获得最佳性能)可以首先直接从请求写入的线程尝试写入