与阻塞 select() 一起使用时,非阻塞套接字真的是非阻塞的吗?

时间:2021-01-31 23:46:47

标签: c sockets select nonblocking

这是一个相当理论化的问题。如果套接字 I/O(readwrite)设置为 O_NONBLOCK,但是此套接字在 fd_set 中设置为 select(),其中 阻塞(等待文件描述符变为可读或可写的事件),那么该套接字无论如何阻塞(由于select())?

为什么我要将套接字设置为非阻塞,即使阻塞(默认)版本一旦变为可读(或可写)(感谢 select()),也不会阻塞,因为 {{1 }} 表示它有数据要读取(或写入),因此套接字能够在不阻塞的情况下使用该数据执行其操作。那么为什么要在 select() 阻塞时设置套接字非阻塞呢?

具有 select() 套接字的示例,但是阻塞 non-block 结合使用:

select()

2 个答案:

答案 0 :(得分:2)

<块引用>

这是一个相当理论化的问题。如果套接字 I/O(读或写)设置为 O_NONBLOCK,但是此套接字在 fd_set 中设置为 select() 阻塞(等待文件描述符变为可读或可写的事件),则该套接字正在阻塞无论如何(由于 select())?

select 正在阻塞。套接字仍然是非阻塞的。

<块引用>

为什么我要将套接字设置为非阻塞,即使阻塞(默认)版本一旦变为可读(或可写)(感谢 select()),也不会阻塞,因为 select() 已经说过了有数据要读取(或写入),因此套接字能够在不阻塞的情况下使用该数据执行操作。

不,不,不!这不是一个安全的假设。不能保证后续的 readwrite 不会阻塞。如果您需要将来保证以后的操作不会阻塞,则必须将套接字设置为非阻塞。

<块引用>

那么为什么在 select() 阻塞时还要设置套接字非阻塞呢?

因为您不希望套接字上的操作被阻塞。 select 函数不能保证未来的操作不会被阻塞,并且人们已经因为过去的假设而被烧毁。

例如,您在 UDP 套接字上执行 select 并表示接收不会阻塞。但是在您调用 recv 之前,管理员会启用之前禁用的 UDP 校验和。猜猜怎么着,如果唯一接收到的数据报上的校验和不正确,那么您的 recv 将被阻止。

除非您认为自己可以预见到类似事情可能发生的所有方式,并且您绝对不能,否则如果您不希望它阻塞,则必须将套接字设置为非阻塞。

答案 1 :(得分:2)

select 忽略文件描述符上的非阻塞标志,因为关注它没有任何意义。

  • select 正在(可能)同时检查多个文件描述符,它们可能具有不同的非阻塞标志。应该注意哪些?
  • select 有自己的显式超时,用于确定调用是阻塞还是非阻塞,还是阻塞了有限的时间。

可以说,在文件描述符上设置非阻塞“状态”标志是一个糟糕的设计——最好在每次调用时指定是阻塞还是非阻塞。如果您使用 recvsend 而不是 readwrite,您实际上可以做到这一点。所有在文件描述符上设置“非阻塞”的真正作用是它进行的调用不指定阻塞或非阻塞、非阻塞。