boost::asio::buffer:如何获取缓冲区大小并防止缓冲区溢出?

10
我有以下两个用于发送和接收数据包的函数。
void send(std::string protocol)
{
    char *request=new char[protocol.size()+1];
    request[protocol.size()] = 0;
    memcpy(request,protocol.c_str(),protocol.size());

    request_length = std::strlen(request);
    boost::asio::write(s, boost::asio::buffer(request, request_length));
}
void receive()
{
    char reply[max_length];
    size_t reply_length = boost::asio::read(s, boost::asio::buffer(reply, request_length));
    std::cout << "Reply is: ";
    std::cout.write(reply, reply_length);
    std::cout << "\n";
}

这些问题涉及到这部分的内容: boost::asio::buffer(reply, request_length)。这里的 request_length 是在发送数据包时设置的字符串长度。如果不知道 request_length,如何检查缓冲区的大小?另外一个问题是如何防止缓冲区溢出?
3个回答

19
要获取缓冲区的大小,可以使用 boost::asio::buffer_size() 函数。然而,在您的示例中,这很可能对您没有什么用处。
正如在缓冲区 概述 中所解释的,Boost.Asio 使用缓冲区类来表示缓冲区。这些类提供了一个抽象并保护 Boost.Asio 操作免受缓冲区溢出的影响。尽管 boost::asio::buffer() 的结果被传递给操作,但元数据(例如缓冲区的大小或其底层类型)不会被传输。此外,这些缓冲区不拥有内存,因此应用程序有责任确保底层内存在缓冲区抽象的生命周期内始终有效。 boost::asio::buffer()函数提供了一种方便的方式来创建缓冲区类,其中缓冲区大小可以从可能的类型中推导出来。当Boost.Asio能够推导出缓冲区长度时,使用生成的缓冲区类型时,Boost.Asio操作不会引发缓冲区溢出。然而,如果应用程序代码指定了缓冲区的大小为boost::asio::buffer(),则应用程序有责任确保该大小不大于底层内存。
在读取数据时,需要一个缓冲区。根本问题是,如果Boost.Asio没有传输大小,那么如何知道要分配多少内存。有几个解决这个问题的方法:
  • Query the socket for how much data is available via socket::available(), then allocate the buffer accordingly.

    std::vector<char> data(socket_.available());
    boost::asio::read(socket_, boost::asio::buffer(data));
    
  • Use a class that Boost.Asio can grow in memory, such as boost::asio::streambuf. Some operations, such as boost::asio::read() accept streambuf objects as their buffer and will allocate memory as is required for the operation. However, a completion condition should be provided; otherwise, the operation will continue until the buffer is full.

    boost::asio::streambuf data;
    boost::asio::read(socket_, data,
                      boost::asio::transfer_at_least(socket_.available()));
    
  • As Öö Tiib suggests, incorporate length as part of the communication protocol. Check the Boost.Asio examples for examples of communication protocols. Focus on the protocol, not necessarily on the Boost.Asio API.

    • In a fixed size protocol, both the data producer and consumer use the same size message. As the reader knows the size of the message, the reader can allocate a buffer in advance.
    • In a variable length protocol, the messages are often divided into two parts: a header and a body. The header is normally fixed size, and can contain various meta-information, such as the length of the body. This allows a reader to read a header into a fixed size buffer, extract the body length, allocate a buffer for the body, then read the body.

      // Read fixed header.
      std::vector<char> data(fixed_header_size);
      boost::asio::read(socket_, boost::asio::buffer(data));
      
      protocol::header header(data);
      network_to_local(header); // Handle endianess.
      
      // Read body.
      data.resize(header.body_length());
      boost::asio::read(socket_, boost::asio::buffer(data));  
      
      protocol::body body(data);
      network_to_local(body); // Handle endianess.    
      

很不错。在消息以分隔符结束的情况下,boost::asio::streambuffboost::asio::read_until() 对我非常有效。 - Matt Munson

6
通常,通信协议使用固定长度的消息或包含头部的消息来告知消息的长度。 Boost.Asio在线文档 包含大量示例和教程,因此您应该从那里开始。维基百科是解释数据传输术语的好来源,boost asio文档不会这样做。

2
我已经浏览了boost asio教程,但那些只是简单地介绍了示例,并没有详细解释函数调用的作用、返回值等。我知道我也可以查看hpp文件,但那就像在干草堆里找针一样,甚至比示例本身更无用。此外,我相信在boost示例/教程中没有提到的很多东西只能在hpp文件中找到,这可能需要多年的实践才能消化。 - pandoragami
@lost_with_coding 或许 boost asio 的示例假定人们熟悉数字通信的概念以及相关术语的含义,例如协议、客户端、服务器、请求等。我建议不要从代码中逆向工程获取这些知识,有更简单的来源可以学习所有这些内容。 - Öö Tiib
@lost_with_coding: 这些示例没有说明API的作用,因为这在参考文档中有详细记录。 Boost.Asio库是复杂的,而不是复杂化的。文档中包含大量信息,花时间熟悉文档中的各个部分可能是值得的。 - Tanner Sansbury
@twsansbury 谢谢你提供的参考链接,非常有用。我从来不知道它的存在。 - pandoragami

1

我觉得你的问题有些混淆,但这可能会有所帮助:

void receive() {
  enum { max_length = 1024 };
  char reply[max_length];
  size_t reply_length;
  std::cout << "Reply is: ";
  while ( (reply_length = ba::read(s, basio::buffer(reply, max_length))) > 0) {
    std::cout.write(reply, reply_length);
  }
  std::cout << "\n";
}

boost::asio::buffer(,) 返回的是什么?我看到它返回了一些东西到 ba::read(s,buffer(,)),但具体是什么呢? - pandoragami
@lost_with_coding:一种代表内存但不拥有内存的类型。[function](http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/reference/buffer.html)帮助适应常用类型,使它们满足许多Boost.Asio操作的类型要求。这个[概述](http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/overview/core/buffers.html)提供了一些信息。 - Tanner Sansbury
在这个例子中,您不必将max_length传递给asio :: buffer()。 (因为asio可以使用sizeof获取它。) - Torkel Bjørnson-Langen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接