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

时间:2013-02-25 05:26:02

标签: c++ boost-asio buffer-overflow

我有以下两个用于发送和接收数据包的功能。

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的情况下检查缓冲区的大小?另一个问题是如何防止缓冲区溢出?

3 个答案:

答案 0 :(得分:15)

要获得缓冲区的大小,可以使用boost::asio::buffer_size()函数。但是,在您的示例中,这很可能对您没用。

如缓冲区overview中所述,Boost.Asio使用缓冲区类来表示缓冲区。这些类提供抽象并保护Boost.Asio操作免受缓冲区溢出的影响。虽然boost::asio::buffer()的结果传递给操作,但不传输元数据,例如缓冲区的大小或其基础类型。此外,这些缓冲区不拥有内存,因此应用程序有责任确保底层内存在缓冲区抽象的整个生命周期内保持有效。

boost::asio::buffer()函数提供了一种创建缓冲区类的便捷方法,其中缓冲区的大小是从可能的类型中推导出来的。当Boost.Asio能够推断出缓冲区长度时,Boost.Asio操作在使用生成的缓冲区类型时不会调用缓冲区溢出。但是,如果应用程序代码将缓冲区的大小指定为boost::asio::buffer(),则应用程序有责任确保大小不大于底层内存。

读取数据时,需要缓冲区。如果Boost.Asio不传输大小,那么基本问题就是如何知道要分配多少内存。这个问题有几个解决方案:

  • 通过socket::available()查询套接字可用的数据量,然后相应地分配缓冲区。

    std::vector<char> data(socket_.available());
    boost::asio::read(socket_, boost::asio::buffer(data));
    
  • 使用Boost.Asio可以在内存中增长的类,例如boost::asio::streambuf。某些操作(例如boost::asio::read()接受streambuf个对象作为其缓冲区,并将按操作所需的内容分配内存。但是,应提供完成条件;否则,操作将继续,直到缓冲区已满。

    boost::asio::streambuf data;
    boost::asio::read(socket_, data,
                      boost::asio::transfer_at_least(socket_.available()));
    
  • Öö Tiib所示,将长度作为通信协议的一部分。检查Boost.Asio examples以获取通信协议的示例。专注于协议,不一定在Boost.Asio API上。

    • 在固定大小的协议中,数据生产者和消费者都使用相同大小的消息。当读者知道消息的大小时,读者可以提前分配缓冲区。
    • 在可变长度协议中,消息通常分为两部分:标题和正文。标题通常是固定大小,并且可以包含各种元信息,例如正文的长度。这允许读者将标题读入固定大小的缓冲区,提取体长,为身体分配缓冲区,然后读取体。

      // 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.    
      

答案 1 :(得分:5)

通常,通信协议使用固定长度的消息或包含告知消息长度的标头的消息。

Boost.Asio online documentation包含大量示例和教程,因此您应该从那里开始。维基百科是解释data transmission术语的良好来源,提升asio文档不会这样做。

答案 2 :(得分: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";
}