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

问题描述

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

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

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

select()

解决方法

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

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

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

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

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

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

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

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

,

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

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

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