c++ - 亚信 : Is there an automatically resizable buffer for receiving input?

标签 c++ boost boost-asio

Asio: Is there an automatically resizable buffer for receiving input?

我事先不知道要接收的尺寸,所以我在标题中发送了这个数量。

我看过http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/example/cpp03/chat/chat_message.hpp对于使用标题的示例,但此示例假定最大正文大小的规范。

查看 asio::buffer 类,我必须提供一些底层缓冲区,因此不灵活。相反,我查看了 asio::streambuf 类,但如下使用它会产生分段/内存错误。

我尝试给出一个最大大小以只读 HEADER_LEN 字节,即标题。

这种做法错了吗?

void do_recv_header() 
{
    asio::streambuf buf(HEADER_LEN);
    asio::async_read(*g_selected_conn, buf, [this, &buf](const system::error_code& ec, std::size_t bytes_transferred)
    {
        if (ec != 0) {
            std::cout << "async_read() error: " << ec.message() << " (" << ec.value() << ") " << std::endl;
            remove_closed_conn(g_selected_conn);
            SetEvent(g_wait_event);
        }
        else {
            std::istream is(&buf);
            int body_len;
            is >> body_len;
            std::cout << body_len << std::endl;
            do_recv_body(body_len);
        }
    });
}

最佳答案

boost::asio::streambuf是一个自动调整大小的缓冲类。这种缓冲区类型通常在启动读取操作时使用,该读取操作的完成取决于数据的内容,而不一定是数据的大小。例如,可以使用 boost::asio::read_until() 读取到换行符,而不知道或指定可以读取多少数据。

如果应用程序协议(protocol)具有包含主体长度的固定大小的 header ,并且 header 后跟可变长度的主体,请考虑使用缓冲区类型,例如 std::vector<> .这将提供与 boost::asio::streambuf 相同级别的灵 active ,同时简化了一些簿记:

std::vector<char> buffer;

// Read header.
buffer.resize(protocol::header_size);
boost::asio::read(socket, boost::asio::buffer(buffer));

// Extract body size from header, resize buffer, then read
// body.
auto body_size = parse_header(buffer);
buffer.resize(body_size);
boost::asio::read(socket, boost::asio::buffer(buffer));

process_body(buffer);

不是如何调整 vector 的大小指示在读取操作中将读取多少数据。使用 streambuf 时,必须直接使用这些操作来管理输入和输出序列:

boost::asio::streambuf streambuf;

// Read header into the streambuf's output sequence.
auto bytes_transferred = boost::asio::read(socket,
  streambuf.prepare(protocol::header_size));
// Commit read data from output sequence into the input
// sequence.
streambuf.commit(bytes_transferred);

// Extract body size from header.  This would likely
// consume all of the streambuf's input sequence.
auto body_size = parse_header(streambuf);
// Clear the input sequence.
streambuf.consume(streambuf.size());

// Ready body into the streambuf's output sequence.
bytes_transferred = boost::asio::read(socket,
  streambuf.prepare(body_size));
// Commit read data from output sequence into the input
// sequence.
streambuf.commit(bytes_transferred);

// Extract all of stream into the body.
process_body(streambuf);

这是一个完整的例子demonstrating这种方法:

#include <array> // std::array
#include <functional> // std::bind
#include <iostream> // std::cout, std::endl
#include <vector> // std::vector
#include <boost/asio.hpp>

// This example is not interested in the handlers, so provide a noop function
// that will be passed to bind to meet the handler concept requirements.
void noop() {}

// The application protocol will consists of a fixed-size header
// containing a std::size_t with the length of the following
// variable length body.  To keep it simple, some details
// are ommitted, such as endian handling.
namespace protocol {
enum
{
  header_size = sizeof(std::size_t)
};
} // namespace protocol

std::vector<char> build_header(const std::string& body)
{
  std::vector<char> buffer(protocol::header_size);
  auto body_size = body.size();
  std::memcpy(&buffer[0], &body_size, sizeof body_size);
  return buffer;
}

std::size_t parse_header(const std::vector<char>& buffer)
{
  return *reinterpret_cast<const std::size_t*>(&buffer[0]);
}

int main()
{
  using boost::asio::ip::tcp;

  // Create all I/O objects.
  boost::asio::io_service io_service;
  tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
  tcp::socket socket1(io_service);
  tcp::socket socket2(io_service);

  // Connect the sockets.
  acceptor.async_accept(socket1, std::bind(&noop));
  socket2.async_connect(acceptor.local_endpoint(), std::bind(&noop));
  io_service.run();
  io_service.reset();

  //  Write a message from socket1 to socket2.
  std::string test_message = "this is a test message";
  {
    auto header = build_header(test_message);

    // Gather header and body into a single buffer.
    std::array<boost::asio::const_buffer, 2> buffers = {{
      boost::asio::buffer(header),
      boost::asio::buffer(test_message)
    }};

    // Write header and body to socket.
    boost::asio::write(socket1, buffers);
  }

  // Read from socket2.
  {
    // Use a vector to allow for re-sizing based on the
    // amount of data needing to be read.  This also reduces
    // on the amount of reallocations if the vector is reused.    
    std::vector<char> buffer;

    // Read header.
    buffer.resize(protocol::header_size);
    boost::asio::read(socket2, boost::asio::buffer(buffer));

    // Extract body size from header, resize buffer, then read
    // body.
    auto body_size = parse_header(buffer);
    buffer.resize(body_size);
    boost::asio::read(socket2, boost::asio::buffer(buffer));

    // Verify body was read.
    assert(std::equal(begin(buffer), end(buffer), 
                      begin(test_message)));
    std::cout << "received: \n"
                 "  header: " << body_size << "\n"
                 "  body: ";
    std::cout.write(&buffer[0], buffer.size());
    std::cout << std::endl;
  }
}

输出:

received: 
  header: 22
  body: this is a test message

关于c++ - 亚信 : Is there an automatically resizable buffer for receiving input?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36304000/

相关文章:

c++ - 在 C/C++ 中快速显示波形

c++ - 如何使用基类的所有派生类填充 vector/列表

c++ - 多个显示器上的 SetConsoleWindowInfo

c++ - 位准确(或多或少)opencv/C++ 平滑函数(imfilter)到 Matlab

c++ - Boost套接字 - 客户端没有从服务器接收所有字节

c++ - 从图中删除边

c++ - 从不在 PIMPL 中提供析构函数(使用 boost scoped_ptr),g++(4.6.1) 不会生成编译错误,为什么?

c++ - 传递将模板参数作为参数默认为 NULL 的 boost::function

c++ - 提升异步套接字和提升::线程

c++ - 我应该看到 std::bind 和 boost::bind 之间的显着差异吗?