【问题标题】:Most elegant way of using recv in C++ / STL?在 C++/STL 中使用 recv 的最优雅方式?
【发布时间】:2015-02-16 07:20:11
【问题描述】:

到目前为止,我所发现的关于使用套接字 (recv) 接收数据的所有内容都是用普通的旧 C 语言编写的。

即使是 C++,它也可以在 std::vector<char>(哇!)或defining a static size(例如vec.resize(BIG_NUMBER))上逐字节接收,然后再接收。

然而,所有这些看起来都非常低效和/或丑陋,所以我想知道,在 C++ 中最有效、正确和优雅的方法是什么? (不是 C)

网络浏览器就是一个很好的例子,说明某人需要在哪里接收未知数量的数据。 (为了示例,忽略 Content-length 标头)

【问题讨论】:

  • 您可以尝试使用带有抽象的库。例如,试试 Poco。
  • 不使用recv,而是使用C++函数?没有更优雅的“使用recv”方式。您必须“不使用它”,而使用其他东西。
  • @sashoalm 不,我知道 recv 是 C API,而不是 C++。我的问题是指在 C 中,您需要自己进行内存分配,而在 C++ 中,可能有一种方法可以避免它;我想知道是否有这样的方法。
  • @AcidShout 所以你问recv是否可以告诉你向量的大小?那么是的,它可以,但是问题标题应该是“找出recv所需的缓冲区大小”或类似的东西。见stackoverflow.com/questions/2862071/…
  • @sashoalm 不,因为链接的问题就是这样做的(或多或少)。我的意思是完全抽象掉内存分配部分(包括大小)。就像std::string 的“魔法”。

标签: c++ sockets stl


【解决方案1】:

我不知道任何 STL 方法可以这样做,但无论如何,STL 与它无关。 在处理旧的 C API 时,可以简单地用 C++ 类包装混乱,使实现对外部开发人员不可见。

我建议编写包含 SOCKET 作为内部成员的 Socket 类,而不是创建一些包装 C 的方法(假设我们谈论的是流套接字!):

std::string Socket::receive (){
char buffer [SOME_SIZE];
int bytesRead;

std::string receivedData;

do{
    bytesRead = recv(socket,buffer,SOME_SIZE-2,0);
    buffer [SOME_SIZE-1] ='/0';
    receivedData += buffer;
} while (bytesRead != 0 && bytesRead != SOCKET_ERROR );

return receivedData;
}

顺便说一句,我怀疑你可能没有完全理解 recv() ,recv() 填充一个缓冲区,只要你反复调用 recv() 就会填充它。 套接字接收的字节数不受缓冲区大小的限制。 您可以使用这个事实将数据附加到 std::string

【讨论】:

  • 非常有用的插图。一些注意事项:提供的代码将无限期地阻塞并耗尽所有套接字输入,直到套接字断开连接或出现错误,而您通常希望指定最大大小或一些要注意的标记字符(例如换行符); bytesread在windows上确实返回int,但是在POSIX系统上是ssize_t0表示有序关闭,-1表示错误(见errno),可以支持'\0'转过来通过放弃buffer[SOMESIZE-1] = '\0' 行并将+= 行替换为receivedData.append(buffer, bytesRead); 来创建套接字。
【解决方案2】:

如果您不介意使用 3rd-party 库,Boost 有很好的模板用于处理 C++ 中的套接字。看看普通的旧 C 套接字 API 函数和他们的函数之间的equivalences;在this 示例中,您将看到如何使用streambufs 来避免为接收缓冲区分配内存。

【讨论】:

  • 我试图找到一种方法来撰写这个答案,但这个答案真的很到位
  • 如果使用 Visual Studio 进行开发,这里有个好消息:现在 Nuget 支持 C++ 第三方库,Boost 就是其中之一。它还非常智能地分离为一个头包 (boost) 和多个库包 (boost_*),因此您不必再关闭整个 Boost 库。如果您只是放下头包并开始编码,链接器会告诉您要下载哪些库包。例如,使用链接示例,我需要 boost_system(用于 boost::asio)、boost_date_time 和 boost_regex。
【解决方案3】:

基本上,对于每个套接字,您需要有两个用户空间缓冲区:一个用于发送数据,另一个用于接收。

接收缓冲区的最佳大小与内核缓冲区大小相同。如果您的缓冲区较小 - 您的应用程序执行更多 recv 调用;如果更大 - 剩余容量可能永远不会被使用。

您使用的有线协议通常具有最大消息大小。您的缓冲区必须至少那么大。 Bandwidth-delay product 可能还需要调整您的最小缓冲区大小。

通常,您希望将缓冲区与套接字一起分配一次,之后不再调整它们的大小。您可能希望将缓冲区存储为char[]std::array<char>std::vector<char>(但避免使用new char[])。

【讨论】:

    猜你喜欢
    • 2020-09-11
    • 2011-05-12
    • 1970-01-01
    • 2018-04-01
    • 1970-01-01
    • 2010-10-12
    • 2013-04-23
    • 2013-11-14
    • 2022-11-21
    相关资源
    最近更新 更多