阻止recv()返回小于请求的字节的情况

recv()库函数手册页提到:

它返回收到的字节数。 它通常返回任何可用的数据,直到请求的金额,而不是等待收到所需的全部金额。

如果我们使用阻塞recv()调用并请求100字节:

 recv(sockDesc, buffer, size, 0); /* Where size is 100. */ 

并且只有50个字节被服务器发送,那么这个recv()被阻塞,直到有100个字节可用,否则它将返回接收50个字节。

情况可能是这样的:

  • 服务器发送仅50个字节后崩溃

  • 错误的协议devise,其中服务器只发送50字节,而客户端期待100,服务器也在等待客户端的回复(即套接字closures连接尚未由服务器启动recv将返回)

我对Linux / Solaris平台感兴趣。 我没有开发环境来自己检查一下。

当内部缓冲区中的数据返回时,recv将返回。 如果请求100个字节,它不会等到有100个字节。

如果你发送100个字节的“消息”,请记住TCP不提供消息,这只是一个流。 如果您正在处理应用程序消息,则需要在应用程序层处理,因为TCP不会这样做。

在调用recv(…,100)时,有很多很多情况下,100字节的send()调用在另一端可能无法完全读取,只有一个recv调用; 这里只是一些例子:

  • 发送TCP堆栈决定捆绑15个写入调用,而MTU碰巧是1460,这取决于到达的数据的时间可能导致客户端的前14个调用取100个字节,而调用取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, nt); 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. } 

行为是由两件事情决定的。 接收到低水位标志以及是否通过MSG_WAITALL标志。 如果你传递这个标志,那么即使服务器崩溃,呼叫也会被阻塞,直到收到请求的字节数。 其他方面,只要在套接字的接收缓冲区中至少有SO_RCVLOWAT字节可用,它就会返回。

SO_RCVLOWAT

设置套接字输入操作要处理的最小字节数。 SO_RCVLOWAT的默认值为1.如果SO_RCVLOWAT设置为较大值,阻塞接收呼叫通常会等待,直到它们接收到低水位值或请求数量中的较小值。 (如果发生错误,信号被捕获,或者接收队列中的下一个数据类型与返回的数据类型(例如带外数据)不同,它们可以返回低于低水位标记)。 该选项采用一个int值。 请注意,并非所有实现都允许设置此选项。

如果您仔细阅读报价,最常见的情况是:

  • 套接字正在接收数据。 那100个字节需要一些时间。
  • recv()调用已经完成。
    • 如果缓冲区中有多于0个字节,则recv()返回可用并不等待的内容。
    • 虽然有0个字节可用,但块和线程系统的粒度决定了多长时间。