阻塞 recv() 返回小于请求字节的情况
Posted
技术标签:
【中文标题】阻塞 recv() 返回小于请求字节的情况【英文标题】:Case when blocking recv() returns less than requested bytes 【发布时间】:2011-01-18 17:44:27 【问题描述】:recv()
库函数手册页提到:
它返回接收到的字节数。它通常返回任何可用的数据,直到请求的数量,而不是等待收到请求的全部数量。
如果我们使用阻塞 recv()
调用并请求 100 字节:
recv(sockDesc, buffer, size, 0); /* Where size is 100. */
如果服务器只发送 50 个字节,那么这个 recv()
会被阻塞,直到 100 个字节可用,否则它将返回接收 50 个字节。
情况可能是这样的:
仅发送 50 个字节后服务器崩溃
糟糕的协议设计,服务器只发送 50 个字节,而客户端期待 100 个字节,服务器也在等待客户端的回复(即,recv 将返回的服务器尚未启动套接字关闭连接)
我对 Linux / Solaris 平台感兴趣。我没有开发环境自己去看看。
【问题讨论】:
【参考方案1】:当内部缓冲区中有数据要返回时,recv 将返回。如果你请求 100 个字节,它不会等到有 100 个字节。
如果您要发送 100 字节的“消息”,请记住 TCP 不提供消息,它只是一个流。如果您正在处理应用程序消息,则需要在应用程序层处理,因为 TCP 不会这样做。
在很多很多情况下,调用 recv(..., 100); 时,只有一个 recv 调用,可能无法在另一端完全读取 100 字节的 send() 调用;这里只是几个的例子:
发送 TCP 堆栈决定将 15 个写入调用捆绑在一起,而 MTU 恰好是 1460,这 - 根据到达数据的时间可能会导致客户端前 14 个调用获取 100 个字节和 15 个。调用以获取 60 个字节 - 最后 40 个字节将在您下次调用 recv() 时出现。 (但如果你用 100 的缓冲区调用 recv ,你可能会得到前一个应用程序“消息”的最后 40 个字节和下一个消息的前 60 个字节)
发送者缓冲区已满,可能是阅读器速度较慢,或者网络拥塞。在某些时候,数据可能会通过,在清空缓冲区时,最后一块数据不是 100 的倍数。
接收器缓冲区已满,而您的应用程序 recv() 该数据,它提取的最后一个块只是部分,因为该消息的整个 100 字节不适合缓冲区。
其中许多场景都很难测试,尤其是在您可能没有很多拥塞或数据包丢失的局域网上 - 随着您提高和降低消息发送/生成的速度,情况可能会有所不同。
无论如何。如果您想从套接字读取 100 个字节,请使用类似
int
readn(int f, void *av, int n)
char *a;
int m, t;
a = av;
t = 0;
while(t < n)
m = read(f, a+t, n-t);
if(m <= 0)
if(t == 0)
return m;
break;
t += m;
return t;
...
if(readn(mysocket,buffer,BUFFER_SZ) != BUFFER_SZ)
//something really bad is going on.
【讨论】:
感谢您的详细解释。只是确认 blocking recv() 调用是否也是如此? (即 recv() 返回的字节数少于请求的字节数) 是的,这适用于阻塞式 recv 调用。【参考方案2】:行为由两件事决定。 recv 低水位标记以及您是否通过MSG_WAITALL
标志。如果您传递此标志,则调用将阻塞,直到收到请求的字节数,即使服务器崩溃。否则,只要套接字的接收缓冲区中至少有 SO_RCVLOWAT
字节可用,它就会返回。
SO_RCVLOWAT
将最小字节数设置为 套接字输入操作的过程。 SO_RCVLOWAT 的默认值为 1.如果SO_RCVLOWAT设置为较大的值,正常阻塞接收调用 等到他们收到 较小的低水位标记值或 要求的金额。 (他们可能会返回 低于低水位线,如果 发生错误,信号被捕获,或 接收中的下一个数据类型 队列与返回的不同, 例如带外数据)。这个选项 取一个 int 值。请注意,并非所有 实现允许此选项 设置。
【讨论】:
SO_RCVLOWAT 是套接字选项。如果我们没有设置任何套接字选项并且没有将标志传递给recv(),并且发出阻塞recv()调用,它会等待所有请求的字节吗? (socket关闭操作也没有启动) 在您在帖子中描述的情况下,它将返回 50 个字节。除非您将MSG_WAITALL
标志传递给 recv
,否则它将不会等待完整的 100 个字节。【参考方案3】:
如果您仔细阅读报价,最常见的情况是:
套接字正在接收数据。这 100 个字节需要一些时间。 进行了recv() 调用。 如果缓冲区中的字节数超过 0 个,recv() 将返回可用的内容并且不等待。 虽然有 0 个字节可用,但它会阻塞,线程系统的粒度决定了它的长度。【讨论】:
谢谢,这意味着如果发出阻塞 recv() 并且数据可用(少于请求),它将返回任何可用的数据。对吗?以上是关于阻塞 recv() 返回小于请求字节的情况的主要内容,如果未能解决你的问题,请参考以下文章