【问题标题】:How does recv work in socket programming?recv 在套接字编程中是如何工作的?
【发布时间】:2021-05-11 08:26:32
【问题描述】:

我试图从高层次上理解 recv()。所以 recv 以“块”的形式获取数据,但我仍然不知道它是如何被精确处理的。示例:

char buffer[1000]; 
int received= recv(sock, buffer, sizeof(buffer), 0)

这是否意味着如果我正在接收大量文件,缓冲区,如果通过 sock 连接可能会反映它在接收变量中存储了 500 个字节,然后在循环中接收另外 300 个字节和所有 800 个字节的数据将在循环结束时存储在缓冲区中(丢失在接收到的变量中,除非考虑在内),或者缓冲区是否需要一个指针来跟踪它上次接收数据的位置以便在下一次迭代中存储它?

【问题讨论】:

  • 不确定我是否完全遵循您的问题,但 recv 不会自行循环。您需要这样做,以及将数据块重新组合成最终应用程序数据所需的任何簿记。所有recv 所做的和类似的事情是将原始数据从套接字传递到应用程序。 man page 可以告诉你更多。
  • 不,这不会发生。当modem接收到数据时,将数据存储在自己的栈中,recv只是读取这个栈并更新栈指针。

标签: c linux sockets serversocket


【解决方案1】:

recv 没有上下文。它只知道它有一些要写入的地址(指针)和一些最大大小 - 然后它会尝试这个。它总是从给定的地址开始写入。例如,如果希望在某些先前接收到的数据之后添加数据,则可以简单地将指针指向先前数据之后的位置,而不是缓冲区的开头。当然 on 应该调整它允许读取的最大大小以不溢出缓冲区。

【讨论】:

    【解决方案2】:

    您问“recv() 是如何工作的?”,因此可能值得简要研究一个更简单的函数,它本质上做同样的事情 - read()

    recv() 的运行方式与read() 函数大致相同。主要区别在于recv() 允许您在最后一个参数中传递标志 - 但无论如何您都不会使用这些标志。

    我的建议是 - 在尝试使用 recv() 从网络套接字读取之前 - 练习在纯文本文件上使用 read()

    这两个函数都返回读取的字节数 - 除非出现错误,否则它们将返回 -1。您应该始终检查这种情况 - 并妥善处理。

    这两个函数也可以返回少于请求的字节数。在recv() 的情况下 - 并从套接字读取 - 这可能是因为另一端还没有发送所有需要的数据。在读取文件的情况下 - 使用 read() - 这可能是因为您已到达文件末尾。

    无论如何...

    您需要跟踪缓冲区中的当前偏移量 - 并在每次读取时更新它。所以声明一个文件范围变量offset

    static off_t offset; static char buffer[1000];

    然后 - 当你的“循环”运行时 - 每次读取后增加偏移量......

    while (1) {
        size_t max_len = sizeof(buffer) - offset;
        ssize_t count = recv(sock, buffer+offset, max_len, 0);
        if (count == -1) {
            switch (errno) {
                case EAGAIN:
                  usleep(20000);
                  break;
                default:
                  perror("Failed to read from socket");
                  close(sock);
                  break;
            }
        }
        if (count == 0) {
            puts("Looks like connection has been closed.");
            break;
        }
        offset += count;
        if (offset >= expected_len) {
            puts("Got the expected amount of data. Wrapping up ...");
        }
    }
    

    注意事项:

    • 使用这种方法,您要么需要事先知道预期的数据量,要么使用特殊的分隔符来标记消息的结尾
    • max_len 变量表示缓冲区中剩余的空间 - 并且(也许不用说)您不应尝试读取比这更多的字节
    • recv() 命令的目标是buffer+offset - 而不是buffer
    • 如果recv() 返回零,AFAIK 这表明另一端已执行“有序关机”。
    • 如果recv()返回-1,你确实需要检查返回码。 EAGAIN 不是致命的 - 只是意味着你需要再试一次。

    【讨论】:

      猜你喜欢
      • 2016-06-29
      • 2013-11-06
      • 1970-01-01
      • 1970-01-01
      • 2013-06-09
      • 1970-01-01
      • 2021-11-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多