我看到了几篇文章比较了select()和poll()或epoll(),也看到了很多指南讨论如何使用select()来处理多个套接字。但是,我找不到一个没有使用select()的非阻塞recv()调用的比较。如果只有1个要读取数据的套接字和1个要写入数据的套接字,是否有理由使用select()调用呢?recv()方法可以设置为不阻塞并在没有数据可用时返回错误(WSAEWOULDBLOCK),那么当您没有其他套接字需要检查时,为什么还要调用select()呢?非阻塞的recv()调用会慢很多吗?
recv()
将立即返回。那么程序应该怎么办?您需要在循环中调用recv()
,直到数据变为可用-这只是出于几乎没有原因而使用CPU。recv()
上旋转并烧毁CPU非常不理想;您更希望进程等待数据变为可用并被唤醒;这就是select()/poll()
和类似函数的作用。sleep()
以避免烧毁CPU也不是一个好的解决方案。这会导致处理过程的高延迟,因为程序无法在数据可用时立即处理数据。select()
等函数可以让您设计工作流程,使一个套接字的慢速不会影响您服务另一个套接字的速度。假设数据从接收套接字快速到达,并且您希望尽快接受和存储在内存缓冲区中。但是发送套接字很慢。当您填满操作系统的发送缓冲区并且send()
返回EWOULDBLOCK时,您可以发出select()
以在接收和发送套接字上等待。select()
将在到达新数据或释放一些缓冲区并且可以向发送套接字写入更多数据之一的情况下结束。
当然,select()
的更实际用途是当您需要从多个套接字读取和/或写入数据时,或者必须在两个套接字之间双向传递数据时。
事实上,select()
告诉您何时已知下一个读取或写入操作将成功,因此,如果您只在允许的情况下尝试读取和写入,则即使没有使套接字非阻塞,程序几乎也能工作!但这仍然是不明智的,因为存在边缘情况,即使select()
报告套接字为“就绪”,下一个操作仍可能阻塞。
另一方面,几乎从不建议使套接字非阻塞并且不使用select()
,原因请参见@Troy的解释。