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

Posted

技术标签:

【中文标题】从套接字读取时如何处理阻塞的 read() 调用?【英文标题】:How to handle blocking read() call when reading from a socket? 【发布时间】:2018-03-04 00:43:35 【问题描述】:

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

以下代码 sn-p 工作并打印与运行相同的输出

$ 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] = '\0';
    if (fputs(buffer, stdout) == EOF)
        error("fputs");

if (n < 0)
    error("reading from socket");

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

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

如果是这样(这里有跑题的风险),并且read() 正在阻塞,我应该在哪里编写在程序等待该调用返回时向服务器发送命令的逻辑?

【问题讨论】:

让您开始:如果对等方断开连接(您不处理该连接),读取套接字将返回零...如果出现错误(您不要处理) 好吧,我就是这么想的。我很难理解如何处理读取调用。如何从用户那里获取输入并将其写入套接字,同时监视读取调用以确保它没有断开连接? 如果您的问题是如何处理等待客户端连接、等待一条或多条客户端消息以及发送响应...同时,请查看“select()”或“poll() " 提供给你。 (这是一个极大的简化,但应该可以帮助您了解要点)使用 select() 或 poll() 您可以给它一个您想要阅读的描述符列表 + 一个时间限制想要等待,当至少有一个列表可供读取时,select 将返回...我认为它是“select 在发生超时时返回,或者其中一个描述符在读取时不会“阻塞” ()"... 换句话说,您可以选择您的套接字描述符和 stdin_fileno,并且它不会返回,直到您可以在没有阻塞的情况下读取一个...阅读手册页并搜索示例。 了解poll(2) 【参考方案1】:

我无法理解的是为什么程序永远不会到达最后的 printf 调用,而是坐在那里,好像在等待来自服务器的更多信息。

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

【讨论】:

如果peer突然死了怎么办?我观察到,如果 peer 死了,那么 read() 会一直等待。【参考方案2】:

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

while ((n = read(sockfd, buffer, sizeof(buffer)-1)) > 0) 

如果您打算用\0 字节来完成它,最好阅读sizeof(buffer)-1,但如果您想通用,请认为您可以接收来自套接字的\0 ,不要假设您一直在阅读文本。许多安全漏洞都来自这样的错误。程序员(错误地)假设数据是 ascii 文本,并且有人利用缓冲区溢出(不是这种情况)或非法的东西,输入大量空字符使其失败。

printf("\nloop\n");
buffer[n] = '\0';
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)s 的线程,以便在它准备好接受更多数据时立即提供更多数据)


if (n < 0)
    error("reading from socket");

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

【讨论】:

以上是关于从套接字读取时如何处理阻塞的 read() 调用?的主要内容,如果未能解决你的问题,请参考以下文章

使用 pd.read_clipboard 复制数据帧时如何处理自定义命名索引?

应用未运行时如何处理远程通知

单元测试时如何处理 API 调用速率限制?

运行 Mongoose 时执行 ajax 调用时如何处理 Node.js 发现

使用app.get调用错误端点时如何处理?

当我的应用程序激活时如何处理推送通知?