缓冲区

从根本上说,I/O涉及将数据从连续的内存区域(称为缓冲区)传输进出。这些缓冲区可以简单地表示为一个由指针和字节大小组成的元组。然而,为了允许高效网络应用程序的开发,Boost.Asio支持散播-聚合操作。这些操作涉及一个或多个缓冲区:

  • 散播读取:将数据接收至多个缓冲区。

  • 聚合写入:传输多个缓冲区。

因此,我们需要一个抽象来表示缓冲区的集合。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。这些类的目的是提供对连续内存的不透明表示,其中:

  • 类型在转换中表现得像std::pair。这意味着mutable_buffer可以转换为const_buffer,但反向转换被禁止。

  • 有防止缓冲区溢出的保护。给定一个缓冲区实例,用户只能创建另一个表示相同内存范围或其子范围的缓冲区。为了提供进一步的安全性,库还包括从数组、boost::array或std::vector的POD元素,或从std::string自动确定缓冲区大小的机制。

  • 通过data()成员函数显式访问底层内存。一般来说,应用程序不需要这样做,但库实现需要将原始内存传递给底层操作系统函数。

最后,可以通过将缓冲区对象放入容器来将多个缓冲区传递给散播-聚合操作(如read()或write())。定义了MutableBufferSequence和ConstBufferSequence概念,以便可以使用像std::vector、std::list、std::array或boost::array这样的容器。

与Iostreams集成的Streambufboost::asio::basic_streambufstd::basic_streambuf派生,以将输入序列和输出序列与一个或多个某种字符数组类型的对象关联,这些对象的元素存储任意值。这些字符数组对象是streambuf对象内部的,但提供直接访问数组元素以便与I/O操作(如套接字的发送或接收操作)一起使用:

  • streambuf的输入序列可通过data()成员函数访问。该函数的返回类型满足ConstBufferSequence要求。

  • streambuf的输出序列可通过prepare()成员函数访问。该函数的返回类型满足MutableBufferSequence要求。

  • 通过调用commit()成员函数,将数据从输出序列的前端传输到输入序列的后端。

  • 通过调用consume()成员函数,从输入序列的前端移除数据。

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

缓冲区序列的逐字节遍历 buffers_iterator<>类模板允许缓冲区序列(即满足MutableBufferSequence或ConstBufferSequence要求的类型)像连续字节序列一样进行遍历。还提供了辅助函数buffers_begin()buffers_end(),其中buffers_iterator<>模板参数会自动推导。

例如,要从套接字读取一行到std::string中,可以写成:

缓冲区字面量 在命名空间boost::asio::buffer_literals中定义的_buf字面量后缀可用于从字符串、二进制整数和十六进制整数字面量创建const_buffer对象。这些缓冲区字面量可以任意长。例如:

与缓冲区字面量关联的内存在程序的整个生命周期内是有效的。这意味着可以安全地将缓冲区与异步操作一起使用:

缓冲区调试 一些标准库实现,例如Microsoft Visual C++ 8.0及更高版本提供的实现,提供了一种称为迭代器调试的功能。这意味着迭代器的有效性在运行时被检查。如果程序尝试使用已失效的迭代器,将触发断言。例如:

Boost.Asio利用这一特性添加缓冲区调试。考虑以下代码:

当你调用异步读取或写入时,需要确保操作的缓冲区在完成处理程序调用之前是有效的。在上面的示例中,缓冲区是std::string变量msg。该变量在栈上,因此在异步操作完成之前会超出作用域。如果你运气好,应用程序将崩溃,但随机失败的可能性更大。

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

此功能在Microsoft Visual Studio 8.0或更高版本和定义了_GLIBCXX_DEBUG的GCC中自动提供。进行这种检查会有性能开销,因此缓冲区调试仅在调试构建中启用。对于其他编译器,可以通过定义BOOST_ASIO_ENABLE_BUFFER_DEBUGGING来启用它。也可以通过定义BOOST_ASIO_DISABLE_BUFFER_DEBUGGING显式禁用。

See Also

buffer, buffers_begin, buffers_end, buffers_iterator, const_buffer, const_buffers_1, mutable_buffer, mutable_buffers_1, streambuf, ConstBufferSequence, MutableBufferSequence, buffers example (c++11).

Last updated