Sockets 使用多个套接字,非阻塞还是使用select阻塞更好?
假设我有一个服务器程序,可以接受来自10个(或更多)不同客户端的连接。客户端随机发送由服务器接收的数据,但可以肯定的是,每次更新至少会有一个客户端发送数据。服务器无法等待信息到达,因为它还有其他处理要做。除了使用异步套接字之外,我还看到两个选项:Sockets 使用多个套接字,非阻塞还是使用select阻塞更好?,sockets,winsock,blocking,nonblocking,Sockets,Winsock,Blocking,Nonblocking,假设我有一个服务器程序,可以接受来自10个(或更多)不同客户端的连接。客户端随机发送由服务器接收的数据,但可以肯定的是,每次更新至少会有一个客户端发送数据。服务器无法等待信息到达,因为它还有其他处理要做。除了使用异步套接字之外,我还看到两个选项: 使所有插座无阻塞。在循环中,在每个套接字上调用recv(),如果没有可用的数据,并且如果我碰巧获得了一些数据,则允许它通过WSAEWOULDBLOCK失败,然后保留它 让插座保持阻塞状态。将所有套接字添加到FD\u集合并调用select()。如果返回值
recv()
,如果没有可用的数据,并且如果我碰巧获得了一些数据,则允许它通过WSAEWOULDBLOCK
失败,然后保留它FD\u集合
并调用select()
。如果返回值为非零(大多数情况下都是非零),则使用FD_ISSET()
遍历所有套接字以找到适当数量的可读套接字,并且只调用可读套接字上的recv()
recv()
函数的调用。从编程的角度来看,第二种方法是一种更大的痛苦,因为所有的FD_集
和FD_集
循环
首选哪种方法(或其他方法)?避免让非阻塞套接字上的recv()
失败的开销值得调用select()
吗
我想我对这两种方法都很了解,并且两种方法都试过了,但我不知道哪种方法被认为是更好的还是最佳的。我建议改用。然后可以启动,并提供一个回调函数,以便在操作完成时调用。更重要的是,由于它只在程序处于可警报等待状态时才会被调用,因此您不必像在线程应用程序中那样担心锁(假设您在主线程上运行锁)
但是,请注意,您确实需要经常输入这种可警报的等待状态。如果这是您的UI线程,请确保在消息循环中使用MWMO\u ALERTABLE
标志。这将使您的回调有机会运行。在非UI线程上,定期调用使您进入可警报等待状态的任何线程
还请注意,模式对话框通常不会进入可警报的等待状态,因为它们有自己的消息循环,该循环不调用MsgWaitForMultipleObjectsEx()
。如果在显示对话框时需要处理网络IO,请在专用线程上执行所有网络IO,该线程会定期进入可警报的等待状态
无论出于何种原因,如果不能使用重叠IO,则一定要使用阻塞select()
。像在无限循环中那样使用非阻塞recv()
,是对CPU时间的不可原谅的浪费。但是,一定要将套接字置于非阻塞模式——否则,如果一个字节到达,而您试图读取两个字节,那么您可能会意外地阻塞
您可能还想考虑使用一个库来抽象细腻的细节。例如,或者。
IO应该完全阻塞,每个连接一个线程,在这种情况下,事件循环本质上是一个OS调度程序,或者IO应该完全非阻塞,在这种情况下,基于select/waitformultipleobjects的事件循环将在应用程序中 所有中间变量都不是很容易维护和出错的 当并发连接数量增加且没有线程上下文切换开销时,完全非阻塞方法的扩展性更好,因此在并发连接数量不固定的情况下,它是首选方法。与完全阻塞方法相比,这种方法具有更高的实现复杂性对于完全非阻塞IO,应用程序的核心是基于select/waitformultipleobjects的事件循环,所有套接字都处于非阻塞模式,所有读/写操作通常在事件循环线程内完成(对于最高性能的写操作,可先从请求写操作的线程直接尝试)谢谢。对于这个实现,我不想使用重叠IO,但感谢您的建议。我会调查的。我理解您关于使用非阻塞模式并选择()的建议。我的主要问题是,我应该无缘无故地调用recv,还是通过调用select()来实现。谢谢你的回复!更新了一点以使其更清晰-只需循环使用
recv()
,CPU就会达到100%,这绝不是一件好事。