阻塞recv()的情况返回少于请求的字节

时间:2010-02-19 10:59:26

标签: c linux sockets solaris

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

  

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

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

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

并且服务器只发送50个字节,然后阻止此recv(),直到100个字节可用或者它将返回接收50个字节。

情景可能是:

  • 服务器在sendign仅50字节后崩溃

  • 糟糕的协议设计,其中服务器只发送50个字节,而客户端期望100个,服务器也在等待客户端的回复(即,套接字关闭连接尚未由recv将返回的服务器启动)

我对Linux / Solaris平台感兴趣。我没有自己查看的开发环境。

3 个答案:

答案 0 :(得分:16)

当内部缓冲区中有数据要返回时,

recv将返回。如果你请求100个字节,它将不会等到有100个字节。

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

有很多很多条件,在调用recv时,只有一次recv调用,另一端可能无法完全读取100字节的send()调用(...,100);这里只是几个示例:

  • 发送TCP堆栈决定捆绑15个写入调用,MTU恰好是1460,这取决于到达数据的时间可能导致客户端前14个调用获取100个字节和15个。调用获取60个字节 - 下次调用recv()时将返回最后40个字节。 (但是如果你用100的缓冲区调用recv,你可能会得到先前应用程序“message”的最后40个字节和下一个消息的前60个字节)

  • 发件人缓冲区已满,读者可能很慢,或网络拥挤。在某些时候,数据可能会通过,而在清空缓冲区时,最后一块数据不是100的倍数。

  • 接收器缓冲区已满,而你的应用程序recv()表示数据,它上拉的最后一个块只是部分,因为该消息的整个100字节不适合缓冲区。

    < / LI>

其中许多场景都很难测试,尤其是在可能没有大量拥塞或数据包丢失的局域网上 - 随着您发送/生成消息的速度上升和下降,情况可能会有所不同。 / p>

反正。如果要从套接字读取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.

}

答案 1 :(得分:7)

行为由两件事决定。 recv低水位标记以及是否通过MSG_WAITALL标志。如果传递此标志,则呼叫将阻塞,直到收到请求的字节数,即使服务器崩溃也是如此。另外,只要套接字的接收缓冲区中至少有SO_RCVLOWAT字节可用,它就会返回。

  

<强> SO_RCVLOWAT

     

设置最小字节数   套接字输入操作的过程。   SO_RCVLOWAT的默认值是   1.如果SO_RCVLOWAT设置为较大的值,则正常阻止接收呼叫   等到他们收到了   较低的水印值或   要求的金额。 (他们可能会回来   如果是低于水位的话   发生错误,捕获信号,或   接收中的下一个数据类型   队列与返回的队列不同,   例如带外数据)。这个选项   取一个int值。请注意,并非所有   实现允许此选项   设定。

答案 2 :(得分:2)

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

  • 套接字正在接收数据。这100个字节需要一些时间。
  • 进行了recv()调用。
    • 如果缓冲区中有超过0个字节,则recv()返回可用但不等待的内容。
    • 虽然有0个字节可用,但它会阻塞,并且线程系统的粒度决定了它的长度。