【问题标题】:recv the first few bytes from a socket to determine buffer size从套接字接收前几个字节以确定缓冲区大小
【发布时间】:2012-09-13 14:35:26
【问题描述】:

我正在使用TCP/IPsocketsc++ 中编写一个分布式系统。

对于我的每条消息,我需要接收前 5 个字节才能知道传入消息的完整长度。

最好的方法是什么?

  1. recv() 只有 5 个字节,然后又是 recv()。如果我选择这个,假设我会在 recv 中获得 0 或 5 个字节是否安全(也就是不写循环继续尝试)?
  2. 使用MSG_PEEK
  3. recv() 一些更大的缓冲区大小,然后读取前 5 个字节并分配最终缓冲区。

【问题讨论】:

  • 除非您处于非阻塞模式,否则您不会得到零字节。 recv() 结果为零表示 EOS,流结束,因此您通常应该关闭套接字。

标签: c++ sockets network-programming


【解决方案1】:

不需要知道任何事情。 TCP 是一种流协议,在任何给定时刻,您都可以获得少至一个字节或多至几兆字节的数据。使用 TCP 套接字的正确且唯一的方法是循环读取。

char buf[4096];        // or whatever

std::deque<char> data;

for (int res ; ; )
{
    res = recv(fd, buf, sizeof buf, MSG_DONTWAIT);

    if (res == -1)
    {
        if (errno == EAGAIN || errno == EWOULDBLOCK)
        {
            break;  // done reading
        }
        else
        {
            // error, break, die
        }
    }
    if (res == 0)
    {
        // socket closed, finalise, break
    }
    else
    {
        data.insert(data.end(), buf, buf + res);
    }
}

循环的唯一目的是将数据从套接字缓冲区传输到您的应用程序。然后,您的应用程序必须独立地决定队列中是否有足够的数据来尝试提取某种更高级别的应用程序消息。

例如,在您的情况下,您将检查队列的大小是否至少为 5,然后检查前五个字节,然后检查队列是否包含完整的应用程序消息。如果不是,则中止,如果是,则提取整个消息,如果从队列的前面弹出则弹出。

【讨论】:

  • 我承认,我对 C++ 有点陌生(来自沉重的 C 背景)。我打算将 recv 缓冲区放入 std::string 中,以便可以使用协议缓冲区对其进行反序列化。将存储在 deque 中的数据转换为字符串是否有很多开销?
  • @Murph:双端队列的关键在于从两端操作都很便宜。处理字符串中的部分数据会相对昂贵,因为您要移动大量数据。但这是一个很小的区别。
  • 但是如果我首先接收大小,正如我所考虑的那样,我可以预先分配正确大小的字符串,然后缓冲区将被连续填充 - 与填充的工作量大致相同双端队列,但它最终是连续的,所以我可以将它传递给我需要的反序列化方法。
  • @Murph:如果您可以接收到所需数量的数据,这将起作用。但是这个习惯用法不太适合非阻塞操作(以及边缘触发的轮询!),您无法选择准备好多少数据。
【解决方案2】:

使用具有两种状态的状态机:

第一州。

在字节到达缓冲区时接收它们。当有 5 个或更多字节时,对前 5 个字节执行检查,并可能处理缓冲区的其余部分。切换到第二种状态。

第二状态。

在字节到达消息末尾时接收和处理它们。

【讨论】:

    【解决方案3】:

    具体回答您的问题:

    1. 假设你会得到 0 或 5 是不安全的。也有可能得到 1-4。循环直到你得到 5 或其他人建议的错误。
    2. 我不会打扰 PEEK,大多数情况下您会阻塞(假设阻塞调用)或获得 5,因此将额外调用跳过到堆栈中。
    3. 这也很好,但增加了复杂性却收效甚微。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-15
      • 2012-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多