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