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

标签 c linux sockets solaris

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

It returns the number of bytes received. It normally returns any data available, up to the requested amount, rather than waiting for receipt of the full amount requested.

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

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

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

场景可能是:

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

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

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

最佳答案

当内部缓冲区中有数据要返回时,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 个字节)

  • 发送端缓冲区已满,可能是读取端速度慢,或者网络拥塞。在某些时候,数据可能会通过,并且在清空缓冲区时最后一 block 数据不是 100 的倍数。

  • 接收器缓冲区已满,而您的应用程序 recv() 数据时,它提取的最后一个 block 只是部分数据,因为该消息的整个 100 字节不适合缓冲区。

其中许多场景都很难测试,尤其是在您可能没有太多拥塞或数据包丢失的 LAN 上 - 当您提高和降低消息发送/生成的速度时,情况可能会有所不同。

无论如何。如果你想从一个套接字中读取 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.

}

关于阻止 recv() 返回少于请求字节的情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2295737/

相关文章:

linux - 如何通过终端查看硬盘上未分配的可用空间

c - 模拟 UNIX 类型的 shell

c 从指针打印

linux - 使用 Linux 将特定数量的文件从一个目录复制到另一个目录

c - UART 16550 和 Linux 内核中的中断

java - 无法在不损坏文件和阻塞 while 循环的情况下保持套接字打开

android - 如何在android中执行多个重复任务

node.js - ZeroMQ 推/拉和 Nodejs 读取流

C/CUDA - 需要帮助编写程序以将图像存储在缓冲区中

c线程并发和内存混合