9

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

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?另一个问题是如何防止缓冲区溢出?

4

3 回答 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 不传输大小,那么基本问题是如何知道要分配多少内存。这个问题有几个解决方案:

  • 通过 查询套接字有多少可用数据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示例以获取通信协议示例。专注于协议,不一定是 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.    
      
于 2013-02-25T14:51:17.400 回答
6

通常,通信协议要么使用固定长度的消息,要么使用包含标明消息长度的标头的消息。

Boost.Asio 在线文档包含大量示例和教程,因此您或许应该从那里开始。维基百科是解释数据传输术语的好来源,boost asio 文档没有这样做。

于 2013-02-25T05:39:13.060 回答
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";
}
于 2013-02-25T05:39:03.367 回答