boost :: asio :: streambuf - 如何重用缓冲区?

时间:2016-05-22 10:12:28

标签: c++ boost boost-asio

我正在实现TCP服务器,该服务器同时使用asio socket.async_read()和boost :: asio :: async_read_until()方法来异步读取socket中的数据。两者都使用相同的处理程序从boost :: asio :: streambuf中读取数据。

通过async_read()调用完美工作的处理程序:

void handle_read(const boost::system::error_code& ec, std::size_t ytes_transferred) )
{
    m_request_buffer.commit(bytes_transferred);
    boost::asio::streambuf::const_buffers_type rq_buf_data = m_request_buffer.data();
    std::vector<uint8_t> dataBytes(boost::asio::buffers_begin(rq_buf_data), boost::asio::buffers_begin(rq_buf_data) + bytes_transferred);

    //process data here

    m_request_buffer.consume(bytes_transferred);
    bytes_transferred = 0;
}

我的服务器取决于数据处理可能会关闭连接或继续通过同一插槽读取。

但是,如果从第2次boost :: asi :: async_read_until()调用调用handle_read(),我在dataBytes中得到一些零,然后有效数据就会出现。

我尝试了一个简单的测试用例,发现在向streambuf和commit()+ consume()写入数据之后,streambuf中的数据仍保留以前的缓冲区。

那么,有没有办法清除boost :: asio :: streambuf中的数据并在boost :: asio :: async_read_until()中重用它?

Live Coliru

如果使用USE_STREAM = 1编译,则实时示例正常工作。但是std :: istream与缓冲区消耗()相比有什么不同?

1 个答案:

答案 0 :(得分:8)

当使用对streambuf进行操作的Boost.Asio操作或使用streambuf的流对象时,例如std::ostreamstd::istream,基础输入和输出序列将妥善管理。如果为操作提供了缓冲区,例如将prepare()传递给读操作或data()传递给写操作,则必须显式处理commit()和{{1} }。

示例中的问题是它违反了API契约,导致未初始化的内存被提交到输入序列。 commit()文档声明:

  

需要前一个调用consume() prepare(x),并且不需要修改输入或输出序列的中间操作。

x >= nstd::ostream之间使用prepare()违反了此合同,因为它会修改输入序列:

commit()

以下是使用带有注释注释的streambuf的完整示例demonstrating

// Prepare 1024 bytes for the output sequence.  The input sequence is
// empty.
boost::asio::streambuf streambuf;
streambuf.prepare(1024);

// prepare() and write to the output sequence, then commit the written
// data to the input sequence.  The API contract has been violated.
std::ostream ostream(&streambuf);
ostream << "1234567890";

// Commit 10 unspecified bytes to the input sequence.  Undefined
// behavior is invoked.
streambuf.commit(10);

输出:

#include <iostream>
#include <vector>
#include <boost/asio.hpp>

int main()
{
  std::cout << "with streams:" << std::endl;
  {
    boost::asio::streambuf streambuf;

    // prepare() and write to the output sequence, then commit the written
    // data to the input sequence.  The output sequence is empty and
    // input sequence contains "1234567890".
    std::ostream ostream(&streambuf);
    ostream << "1234567890";

    // Read from the input sequence and consume the read data.  The string
    // 'str' contains "1234567890".  The input sequence is empty, the output
    // sequence remains unchanged.
    std::istream istream(&streambuf);
    std::string str;
    istream >> str;
    std::cout << "str = " << str << std::endl;

    // Clear EOF bit.
    istream.clear();

    // prepare() and write to the output sequence, then commit the written
    // data to the input sequence.  The output sequence is empty and
    // input sequence contains "0987654321".
    ostream << "0987654321";

    // Read from the input sequence and consume the read data.  The string
    // 'str' contains "0987654321".  The input sequence is empty, the output
    // sequence remains unchanged.
    istream >> str;
    std::cout << "str = " << str << std::endl;
  }

  std::cout << "with streams and manual operations:" << std::endl;
  {
    boost::asio::streambuf streambuf;

    // prepare() and write to the output sequence, then commit the written
    // data to the input sequence.  The output sequence is empty and
    // input sequence contains "1234567890".
    std::ostream ostream(&streambuf);
    ostream << "1234567890";

    // Copy 10 bytes from the input sequence.  The string `str` contains
    // "1234567890".  The output sequence is empty and the input
    // sequence contains "1234567890".
    auto data = streambuf.data();
    std::string str(boost::asio::buffers_begin(data),
                    boost::asio::buffers_begin(data) + 10);
    std::cout << "str = " << str << std::endl;

    // Consume 10 bytes from the input sequence.  The input sequence is
    // now empty.
    streambuf.consume(10);

    // prepare() and write to the output sequence, then commit the written
    // data to the input sequence.  The output sequence is empty and
    // input sequence contains "0987654321".
    ostream << "0987654321";

    // Copy 10 bytes from the input sequence.  The string `str` contains
    // "0987654321.  The output sequence is empty and the input
    // sequence contains "0987654321".    
    data = streambuf.data();
    str.assign(boost::asio::buffers_begin(data),
               boost::asio::buffers_begin(data) + 10);
    std::cout << "str = " << str << std::endl;

    // Consume 10 bytes from the input sequence.  The input sequence is
    // now empty.
    streambuf.consume(10);
  }
}

有关streambuf使用的更多信息,请考虑阅读this answer。