为什么recv不阻塞直到接收到所有数据?
为什么recv
这个系统调用不直接等到所有数据都接收到再返回呢?我每次看到recv
的调用,都是在一个循环里,不停地调用recv
,直到所有数据都到齐。那为什么不让recv
一开始就一直等着呢?
3 个回答
2
你可以把socket的标志设置为阻塞模式或者非阻塞模式。不过,你提到的具体情况其实和这两种模式没有关系。
让一个网络功能默认按照你描述的方式运行是没有意义的——如果数据流永远不结束,那程序是不是就永远也不能结束?从表面上看,这样的默认行为似乎不太健康。
可以阅读一下这个链接,来了解一下阻塞和非阻塞的输入输出。
4
在大多数情况下,你并不知道“所有数据”到底有多少。举个例子,如果你是在使用一种按行接收数据的协议,那么一行数据可能是10个字节长,也可能是65个字节长。
9
你可以请求让接收(recv)操作一直等到所有数据都接收完,这可以通过使用 MSG_WAITALL
这个标志来实现。不过,如果在这个过程中有信号到达,那么已经接收到部分数据的系统调用就不能自动重新开始接收剩下的数据。因此,即使使用了 MSG_WAITALL
,在某些情况下,recv 调用可能会在缓冲区还没满的时候就返回,这时你需要做好处理这种情况的准备。考虑到这一点,很多人选择简单地循环处理,而不去使用像 MSG_WAITALL
这样不太为人所知的标志。
至于为什么默认是这样的,有几个原因:
- 很多时候你其实是想接收部分数据的。例如,当你逐步显示数据时,或者把数据转发到其他地方,或者数据太大,无法一次性在内存中缓存。毕竟,如果你只是立即写入文件,你在意的是分成200次写入,还是150次写入吗?
- 有时候你一开始甚至不知道需要多少数据。比如
telnet
协议,在 BSD 套接字 API 设计的时候非常流行。你通常是一次接收几字节数据,没有长度字段告诉你预计会接收到多少数据,而且你需要立即显示这些数据。在这种情况下,等到填满缓冲区再处理是没有意义的。类似的,像 SMTP 或 IMAP 这样的行导向协议,你在接收到所有命令之前也不知道命令的长度。 recv
通常用于数据报套接字,它接收一个单独的数据报,即使这个数据报比提供的缓冲区小。对于流式套接字,自然的做法就是尽可能多地返回数据,而不去等待。
但最重要的是,由于你无论如何都需要准备好处理部分缓冲区,因此让人们默认就要处理这个问题是很好的,这样他们可以在循环中早早发现潜在的错误,而不是等到信号在不合适的时刻到达时才暴露出来。