在Winsock中使用select时如何处理套接字同时读写的情况?
关于select()处理套接字多事件的疑问
我之前看过一段示例代码,用来展示select()如何同时处理多个事件,代码如下:
FD_SET readfds; FD_SET writefds; FD_SET exceptfds; FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); FD_SET(someRandomSocket, &readfds); FD_SET(someRandomSocket, &writefds); FD_SET(someRandomSocket, &exceptfds); timeval timeout; timeout.tv_sec = ...; timeout.tv_usec = ...; int total = select(0, &readfds, &writefds, &exceptfds, &timeout); if (total == -1) { // select()调用出错,按需处理... } else if (total == 0) { // 超时,按需处理... } else { if (FD_ISSET(someRandomSocket, &exceptfds) ) { // 套接字有带外数据、非阻塞connect()失败,或其他套接字错误,按需处理... } if (FD_ISSET(someRandomSocket, &readfds) ) { // 套接字有入站数据,或已优雅断开连接,按需处理... } if (FD_ISSET(someRandomSocket, &writefds) ) { // 套接字可写,或非阻塞connect()成功,按需处理... } }
但我有个疑问:当套接字同时存在可读数据和可写状态时,怎么单独读取或发送数据?比如,当FD_ISSET(someRandomSocket, &readfds)和FD_ISSET(someRandomSocket, &writefds)同时为真时:
- 要是只想读取数据,调用
recv(someRandomSocket, buf, 8192, 0)能正常获取可读数据吗? - 要是只想发送数据,调用
send(someRandomSocket, buf, 8192, 0)能正常发送数据吗?
解答
完全可以正常执行,select()的作用只是告知你套接字当前处于可读/可写/异常状态,并不会限制你后续对套接字的操作:
- 当
readfds触发时,说明内核接收缓冲区里有数据,此时调用recv()肯定能读到数据(除非在你调用前对方刚好把数据收走,但这种场景极少),哪怕writefds同时触发也不影响读取操作的有效性。 - 当
writefds触发时,说明内核发送缓冲区有空闲空间,此时调用send()能把数据写入缓冲区(只要数据量不超过剩余空间),同样不受readfds触发的影响。
需要注意的是:
- 如果套接字是阻塞模式,
recv()/send()在没有数据/缓冲区满时会阻塞,但既然select()已经告知了可读/可写状态,这种阻塞不会发生(除非状态在select()返回后瞬间变化,但概率极低)。 - 如果是非阻塞模式,哪怕
select()返回可读/可写,recv()/send()也可能返回EAGAIN/EWOULDBLOCK,这时候你需要下次再通过select()监听后重试。
另外,如果你只想处理其中一种事件,完全可以只执行对应的recv()或send(),不用管另一个状态是否触发——比如你只想读,就直接调用recv(),忽略可写状态即可,不会有任何问题。
内容的提问来源于stack exchange,提问作者just_code_dog
相关产品推荐
相关产品推荐

