从套接字读取时如何处理阻塞的 read() 调用?

How to handle blocking read() call when reading from a socket?

出于自学目的,我正在用 C 语言编写一个简单的 IRC 客户端程序,但在调用从套接字文件描述符读取时无法理解 read() 函数的行为。

以下代码片段有效并打印与 运行

相同的输出

$ echo "NICK gertrudes\r\nUSER a 0 * d\r\n" | nc chat.freenode.net 6667

在终端中,这与我的程序到目前为止打印的相同:

while ((n = read(sockfd, buffer, sizeof(buffer)-1)) > 0) {
    printf("\nloop\n");
    buffer[n] = '[=10=]';
    if (fputs(buffer, stdout) == EOF)
        error("fputs");
}
if (n < 0)
    error("reading from socket");

printf("out of the loop\n");

我不明白的是为什么程序永远不会到达最后的 printf 调用,而是坐在那里,好像在等待来自服务器的更多信息。这是否意味着最后的回复比 0 长并且 IRC 服务器在我发送另一个命令之前不会发送任何新内容?

如果是这样(这里有跑题的风险),并且 read() 正在阻塞,那么在程序等待对 return?

What I fail to understand is why the program never gets to the final printf call, and rather sits there as if waiting for more from the server.

正在等待来自服务器的更多信息。 read() 将 return 在对等方断开连接时归零,之前不会。

尽管您的程序不完整,但您错误地假设了几件事。让我们在您的代码中注释这些内容。

while ((n = read(sockfd, buffer, sizeof(buffer)-1)) > 0) {
  • 如果你打算用[=15=]一个字节来完成它,读sizeof(buffer)-1很好,但认为你可以接收 a [=15=] 来自套接字,如果你想笼统,不要假设你总是在阅读文本。许多安全漏洞都来自这样的错误。程序员(错误地)假设数据是 ascii 文本,有人利用缓冲区溢出(事实并非如此)或一些非法的东西,提供大量空字符使其失败。

    printf("\nloop\n");
    buffer[n] = '[=11=]';
    if (fputs(buffer, stdout) == EOF)
    
  • 这是一个非常常见的错误...当你在缓冲区的末尾放置一个 \n 时,你习惯看到, stdio 会在看到最后一个缓冲区之前立即打印所有内容。好吧,为了实现这一点,stdio 检查描述符是否与终端相关联(通过 ioctl(2) 调用或对 isatty(3) 的调用)。对于套接字不再如此,因此可能您的缓冲区已被复制到 stdio 缓冲区,并且 stdio 正在等待缓冲区填充,或者您在调用 write(2) 发送之前用 fflush(3) 显式刷新缓冲区上面的所有数据。

        error("fputs");
    
  • 在这一点上做一个fflush(stdout);,这样你就可以确定你所有的数据都被发送到对端,然后再继续,或者根本不使用stdio(使用简单的write(2) 调用,直到您足够熟练地准备一个 select(2) 套接字上的线程,以便在它准备好接受更多数据时立即提供更多数据)

    }
    if (n < 0)
        error("reading from socket");
    
    printf("out of the loop\n");