[关闭]
@john-lee 2021-01-02T13:35:15.000000Z 字数 3096 阅读 706

缓冲(Buffers)

Boost.Asio


从根本上讲,I/O 涉及数据在内存的连续区域(称为缓冲区)之间传输。这些缓冲区可以简单地表示为由指针和字节大小组成的元组。但是,为了允许开发高效的网络应用程序,Boost.Asio 包括对散点收集(scatter-gather)操作的支持。这些操作涉及一个或多个缓冲区:

因此,我们需要一个抽象来表示缓冲区的集合。Boost.Asio 中使用的方法是定义一个类型(实际上是两种类型)来表示单个缓冲区。这些可以存储在容器中,该容器可以传递给散点收集操作。

除了将缓冲区指定为指针和大小(以字节为单位)之外,Boost.Asio 还区分了可修改内存(称为可变内存)和非可修改内存(其中后者从存储中创建为 const 限定变量)。因此,这两种类型可以定义如下:

    typedef std::pair<void*, std::size_t> mutable_buffer;
    typedef std::pair<const void*, std::size_t> const_buffer;

在这里,mutable_buffer可转换为const_buffer,但相反方向的转换无效。

但是,Boost.Asio 没有使用上述定义,而是定义两个类:mutable_bufferconst_buffer。这些目标是提供连续内存的不透明表示形式,其中:

最后,通过将缓冲区对象放入容器中,可以将多个缓冲区传递给分散聚集操作(如read()或 write())。MutableBufferSequenceConstBufferSequence概念已经定义好,这样就可以使用诸如std::vectorstd::liststd::arrayboost::array之类的容器。

与Iostreams集成的Streambuf

boost::asio::basic_streambuf派生自std::basic_streambuf,用于将输入序列和输出序列与一个或多个字符数组类型的对象相关联,这些对象的元素存储任意值。这些字符数组对象是 streambuf 对象的内部对象,但提供了对数组元素的直接访问,以允许它们用于 I/O 操作,例如套接字的发送或接收操作:

  • streambuf的输入序列可以通过data()成员函数访问。此函数的返回类型满足ConstBufferSequence要求。
  • streambuf的输出序列可以通过prepare()成员函数访问。此函数的返回类型满足MutableBufferSequence要求。
  • 通过调用commit()成员函数,数据从输出序列的前面传输到输入序列的后面。
  • 通过调用consume()成员函数,从输入序列的前面删除数据。

streambuf构造函数接受一个size_t参数,该参数指定输入序列和输出序列的大小之和的最大值。如果成功,任何将内部数据增长到超出此限制的操作都将抛出std::length_error异常。

缓冲序列的字节遍历

buffers_iterator<>类模板允许遍历缓冲区序列(即满足MutableBufferSequenceConstBufferSequence要求的类型),就好像它们是连续的字节序列一样。还提供了名为buffers_begin()buffers_end()的助手函数,其中buffers_iterator<>模板参数是自动推导出来的。

例如,要从套接字读取单行并将其读入std::string,可以编写:

  1. boost::asio::streambuf sb;
  2. ...
  3. std::size_t n = boost::asio::read_until(sock, sb, '\n');
  4. boost::asio::streambuf::const_buffers_type bufs = sb.data();
  5. std::string line(
  6. boost::asio::buffers_begin(bufs),
  7. boost::asio::buffers_begin(bufs) + n);
缓冲区调试

某些标准库的实现(例如和 Microsoft Visual C++ 8.0 和更高版本一起搭载的库)提供了一个称为迭代器调试的特性。这意味着在运行时检查迭代器的有效性。如果程序尝试使用已失效的迭代器,则会触发断言。例如:

  1. std::vector<int> v(1)
  2. std::vector<int>::iterator i = v.begin();
  3. v.clear(); // invalidates iterators
  4. *i = 0; // assertion!

Boost.Asio 利用此功能添加缓冲区调试。请考虑以下代码:

  1. void dont_do_this()
  2. {
  3. std::string msg = "Hello, world!";
  4. boost::asio::async_write(sock, boost::asio::buffer(msg), my_handler);
  5. }

调用异步读或写时,需要确保操作的缓冲区在调用完成处理程序之前是有效的。在上面的示例中,缓冲区是std::string变量msg。这个变量在栈上,因此在异步操作完成之前它就超出了范围。如果幸运的话,应用程序会崩溃,但随机失败的可能性更大。

启用缓冲区调试时,Boost.Asio 将迭代器存储到字符串中,直到异步操作完成,然后取消引用以检查其有效性。在上面的示例中,您将在 Boost.Asio 尝试调用完成处理程序之前观察到断言失败。

此功能对 Microsoft Visual Studio 8.0 或更高版本可用,而 GCC 需要定义了_GLIBCXX_DEBUG时才可用。此检查会带来性能开销,因此仅在调试生成中启用缓冲区调试。对于其他编译器,可以通过定义 BOOST_ASIO_ENABLE_BUFFER_DEBUGGING来启用它。也可以通过定义 BOOST_ASIO_DISABLE_BUFFER_DEBUGGING来显式禁用它。

另请参阅

buffer, buffers_begin, buffers_end, buffers_iterator, const_buffer, const_buffers_1, mutable_buffer, mutable_buffers_1, streambuf, ConstBufferSequence, MutableBufferSequence, 缓冲区示例 (C++03),缓冲区示例 (c++11)。


Copyright © 2003-2020 Christopher M. Kohlhoff

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注