我有记忆,当我们想在套接字描述符上使用 select() 时,这个套接字应该提前设置为 NONBLOCKING。
但是今天,我读了一个源文件,其中似乎没有将套接字设置为非阻塞的行我的记忆是否正确?
谢谢!
我有记忆,当我们想在套接字描述符上使用 select() 时,这个套接字应该提前设置为 NONBLOCKING。
但是今天,我读了一个源文件,其中似乎没有将套接字设置为非阻塞的行我的记忆是否正确?
谢谢!
黄昏的想法是正确的
通常,您不需要将套接字设置为非阻塞即可在 select() 中使用它。
如果您的内核在 select() 方面符合 POSIX,则这是正确的。不幸的是,有些人使用 Linux,但事实并非如此,正如 Linux select() 手册页所说:
在 Linux 下,select() 可能会将套接字文件描述符报告为“准备好读取”,但随后会出现读取块。例如,当数据到达但检查时校验和错误并被丢弃时,可能会发生这种情况。可能存在文件描述符被虚假报告为就绪的其他情况。因此,在不应阻塞的套接字上使用 O_NONBLOCK 可能更安全。
2011 年 6 月 18 日星期六或前后在 lkml 上对此进行了讨论。一名内核黑客试图证明不符合 POSIX 的合理性。他们在方便的时候尊重 POSIX,在不方便的时候亵渎它。
他争辩说“可能有两个读者,第二个会阻止。” 但是这样的应用程序缺陷是不安全的。内核预计不会防止应用程序缺陷。内核有一个明确的职责:在 select() 之后的第一个 read() 的所有情况下,内核必须返回至少 1 个字节、EOF 或错误;但永远不要阻止。至于 write(),在写入之前,您应该始终测试套接字是否被 select() 报告为可写。这保证您可以写入至少一个字节,否则会出错;但永远不要阻止。让 select() 帮助你,不要盲目写希望你不会阻塞。Linux 黑客对极端案例等的抱怨,是“我们懒得解决难题”的委婉说法。
假设您读取了一个串行端口集:
最小 N;使用 -icanon,为完成读取设置最少 N 个字符
时间N;使用 -icanon,将读取超时设置为 N 十分之一秒
最少 250 次 1
在这里,您需要 250 个字符的块,或十分之一秒的超时。当我在 Linux 上以非阻塞模式尝试此操作时,读取每个字符都会返回,从而影响 CPU。有必要让它处于阻塞模式以获得记录的行为。
因此,有充分的理由将阻塞模式与 select() 一起使用,并期望您的内核符合 POSIX。
但是如果您必须使用 Linux,Jeremy 的建议可能会帮助您应对它的一些内核缺陷。
这取决于。将套接字设置为非阻塞会做几件事:
如果套接字上没有可读取的内容,则立即生成read()
/返回,而不是阻塞。recv()
如果您正在使用select()
,这可能不是问题。只要您只在select()
告诉您可读时才从套接字读取,就可以了。
如果内核缓冲区中没有足够的空间,则进行/返回部分(或零)写入,而不是阻塞write()
。send()
这个很棘手。如果您的应用程序是为处理这种情况而编写的,那就太好了,因为这意味着当客户端读取缓慢时,您的应用程序不会阻塞。但是,这意味着您的应用程序需要将可写数据临时存储在其自己的应用程序级缓冲区中,而不是直接写入套接字,并有选择地将具有挂起写入的套接字放置在writefds
集合中。根据您的应用程序是什么,这可能是救命稻草,也可能是一个巨大的复杂问题。谨慎选择。
如果在连接套接字之前设置,则connect()
在实际建立连接之前立即返回。
同样,如果您的应用程序需要与可能响应缓慢的主机建立连接,同时继续响应其他套接字,这有时很有用,但如果您不小心如何处理这些半连接的套接字,则可能会导致问题。通常最好避免(如果有的话,只在连接后将套接字设置为非阻塞)。
通常,您不需要将套接字设置为非阻塞即可在select()
. 系统调用已经允许您以基本的非阻塞方式处理套接字。但是,某些应用程序将需要非阻塞写入,这就是仍然需要标志的目的。
通常当你使用 select() 时,你使用它是一个事件循环的基础;并且在使用事件循环时,您希望事件循环仅在 select() 内部阻塞,而在其他任何地方都不会阻塞。(这样做的原因是,只要在它正在处理的任何套接字上有事情要做时,您的程序总是会唤醒 - 例如,如果您的程序在套接字 A 的 recv() 中被阻塞,它将是无法处理来自套接字 B 的任何数据,直到它首先从套接字 A 获得一些数据以将其唤醒;反之亦然)。
因此,最好在使用 select() 时将所有套接字设置为非阻塞。这样,您的程序就没有机会在一个套接字上被阻塞并在很长一段时间内忽略其他套接字。
如果您提供的数据多于可以装入套接字发送缓冲区的数据,则 send() 和 write() 会阻塞。通常在 select() 编程中,您不想阻塞除 select() 之外的任何地方,因此您使用非阻塞模式。
对于某些 Windows API,使用非阻塞模式确实很重要。