@john-lee
2021-01-02T13:35:15.000000Z
字数 3096
阅读 706
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_buffer
和const_buffer
。这些目标是提供连续内存的不透明表示形式,其中:
std::pair
一样。也就是说,mutable_buffer
可转换为const_buffer
,但不允许相反的转换。boost::array
或std::vector
或std::string
自动确定缓冲区大小的机制。data()
成员函数显式访问基础内存。通常,应用程序永远不需要这样做,但库实现需要将原始内存传递给底层操作系统函数。最后,通过将缓冲区对象放入容器中,可以将多个缓冲区传递给分散聚集操作(如read()或 write())。MutableBufferSequence
和ConstBufferSequence
概念已经定义好,这样就可以使用诸如std::vector
、std::list
、std::array
或boost::array
之类的容器。
类boost::asio::basic_streambuf
派生自std::basic_streambuf
,用于将输入序列和输出序列与一个或多个字符数组类型的对象相关联,这些对象的元素存储任意值。这些字符数组对象是 streambuf 对象的内部对象,但提供了对数组元素的直接访问,以允许它们用于 I/O 操作,例如套接字的发送或接收操作:
ConstBufferSequence
要求。MutableBufferSequence
要求。streambuf构造函数接受一个size_t
参数,该参数指定输入序列和输出序列的大小之和的最大值。如果成功,任何将内部数据增长到超出此限制的操作都将抛出std::length_error
异常。
buffers_iterator<>
类模板允许遍历缓冲区序列(即满足MutableBufferSequence
或ConstBufferSequence
要求的类型),就好像它们是连续的字节序列一样。还提供了名为buffers_begin()
和buffers_end()
的助手函数,其中buffers_iterator<>
模板参数是自动推导出来的。
例如,要从套接字读取单行并将其读入std::string,可以编写:
boost::asio::streambuf sb;
...
std::size_t n = boost::asio::read_until(sock, sb, '\n');
boost::asio::streambuf::const_buffers_type bufs = sb.data();
std::string line(
boost::asio::buffers_begin(bufs),
boost::asio::buffers_begin(bufs) + n);
某些标准库的实现(例如和 Microsoft Visual C++ 8.0 和更高版本一起搭载的库)提供了一个称为迭代器调试的特性。这意味着在运行时检查迭代器的有效性。如果程序尝试使用已失效的迭代器,则会触发断言。例如:
std::vector<int> v(1)
std::vector<int>::iterator i = v.begin();
v.clear(); // invalidates iterators
*i = 0; // assertion!
Boost.Asio 利用此功能添加缓冲区调试。请考虑以下代码:
void dont_do_this()
{
std::string msg = "Hello, world!";
boost::asio::async_write(sock, boost::asio::buffer(msg), my_handler);
}
调用异步读或写时,需要确保操作的缓冲区在调用完成处理程序之前是有效的。在上面的示例中,缓冲区是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)