为什么recv函数不会一直阻塞,直到接收完所有数据?

8
为什么 recv 系统调用不能一直阻塞到所有数据都接收完毕?每当我看到一个 recv 调用时,它总是在一个 while 循环中,不断调用 recv 直到所有的数据都在那里。为什么不一开始就让 recv 阻塞呢?
3个回答

9
你可以使用 MSG_WAITALL 标志请求 recv 阻塞,直到接收到所有数据。然而,如果有信号到达,已经执行了一些工作(即接收部分数据)的系统调用是不会自动重新启动以接收剩余数据的。因此,即使使用了 MSG_WAITALL,在缓冲区填满之前,recv 调用仍可能返回,在这种情况下,你必须准备处理这些情况。鉴于此,许多人选择循环而不烦恼于像 MSG_WAITALL 这样的不知名标志。

至于为什么默认情况下会这样,有几个原因:

  • 通常情况下,你希望接收部分数据。例如,如果你正在逐步显示随着数据的到来而增量显示的数据,或者将数据代理到其他地方,或者数据太大无法一次性缓冲整个数据等情况。毕竟,如果你只是立即写入文件,那么你在200次写入中是否担心将其拆分成150次呢?
  • 有时你甚至不知道需要多少数据。考虑 telnet 协议,在设计 BSD sockets API 时很流行。通常会每次接收少量字节,没有长度字段告诉你需要期望多少数据,并且还需要立即显示该数据。在这里阻塞直到填充缓冲区是没有意义的。同样适用于面向行的协议,如 SMTP 或 IMAP - 直到接收所有内容之前,你不知道命令的长度。
  • recv 通常用于数据报套接字,其中它接收单个数据报,即使它比提供的缓冲区小得多。流套接字的自然扩展就是在不等待的情况下返回尽可能多的数据。

但最重要的是,由于你必须准备处理部分缓冲区 任何情况,因此强制人们默认处理它是很好的,这样他们会尽早发现循环中的错误-而不是让它们一直隐藏,直到信号在不幸的时刻到达。


4

在大多数情况下,你不知道“所有数据”的数量有多少。例如,如果你正在接收行定向协议的数据,则一行可能长达10个字节或65个字节。


2

您可以将套接字标志更改为阻塞或非阻塞。您的具体情况实际上与阻塞或非阻塞无关。

默认情况下,使网络功能按照您描述的方式运行毫无意义 - 如果流永远不会结束...那么程序永远不会结束吗?乍一看,这似乎不是健康的默认行为。

阅读http://www.scottklement.com/rpg/socktut/nonblocking.html以熟悉阻塞和非阻塞IO。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接