概述
从根本上讲,I/O涉及在连续内存区域(叫做缓冲区)之间传递数据。这些缓冲区可以简单地被表示为一个由指针和字节数组成的元组。然而,为了允许开发高效率的网络应用,Boost.Asio为scatter-gather操作提供支持。这些操作涉及一个或多个缓冲区:
- 一个scatter-read接收数据到多个缓冲区。
- 一个gather-write传输多个缓冲区。
因此我们需要一个抽象来表达一组缓冲区。Boost.Asio使用的方法是定义一个类型(实际上是两个类型)来表示一个缓冲区。这些缓冲区可以被存储在一个容器中,容器可以被传递给scatter-gather操作。
除了将缓冲区设置为一个指针和字节数之外,Boost.Asio还在可修改内存和不可修改内存(后者被从一块const对象指向的内存创建)之间做了区别。因此,这两种类型定义如下:
typedef std::pair<void*, std::size_t> mutable_buffer;
typedef std::pair<const void*, std::size_t> const_buffer;
这里,一个可变的buffer可以被转换成一个const buffer,但反方向转换是无效的。
然而,Boost.Asio并不完全按上面那样定义,而是定义两个类:可变的buffer和const buffer。这些类的目标是提供一个连续内存的不透明表示,其中:
- 类型在转换中的行为类似于std::pair。也就是说,一个可变buffer被转换成一个const buffer,但反方向转换是不被允许的。
- 有防止缓冲区溢出的保护机制。给定一个缓冲区实例,用户只能创建另一个缓冲区,泳衣代表相同内存区域,或者这个内存区域的子区域。为了提供更进一步的安全性,库还包含从数组、boost::array 、包含POD元素的vector、或者一个std::string自动确定缓冲区宽度的机制。
- 在缓冲区之下的内存明确可以用data()函数访问。通常应用程序不应该需要调用这个函数,但是通过库实现传递raw内存到底层操作系统函数中是必须的。
最终,多个缓冲区能被传递到scatter-gather操作中(例如read() write()),通过添加buffer对象到一个容器中。MutableBufferSequence 和ConstBufferSequence 概念已经被定义,以便例如std::vector, std::list, std::array or boost::array这样的容器可以被使用。
Streambuf for Integration with Iostreams
类 boost::asio::basic_streambuf 继承于 std::basic_streambuf,用于将输入队列和输出队列与一个或多个字符数组类型对象关联起来,这些数组类型对象的元素可以存储任意值。这些字符数组对象是streambuf对象的内部对象,但是直接访问数组元素的方式也是被提供的,为了允许他们被用在I/O操作上,例如在socket上的send或receive函数:
- streambuf的输入队列是可访问的,通过data()成员函数。这个函数的返回类型满足ConstBufferSequence 要求。
- streambuf的输出队列是可访问的,通过prepare()成员函数。这个函数的返回类型满足MutableBufferSequence 要求。
- 通过调用commit()成员函数,数据被从输出队列的首部传输到输入队列的尾部。
- 通过调用 consume() 成员函数,数据被从输入队列的头部删除。
streambuf构造函数接受一个size_t 参数,用以指定输入队列和输出队列的字节总数的最大值。任何想要的操作,如果成功,内部数据的增长超过了这个限制,就会抛出一个 std::length_error异常。
Bytewise Traversal of Buffer Sequences
buffers_iterator<>类模板允许缓冲区队列(即类型满足 MutableBufferSequence 或 ConstBufferSequence要求)被遍历,就好像他们是一个连续字节序列一样。帮助函数buffers_begin() ,buffers_end()也被提供,在这些函数里,buffers_iterator<>模板参数会被自动推导出来。
作为一个例子,为了从socket中读取一行,并且写入到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);
Buffer Debugging
一些标准库实现,例如随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会保存一个迭代器到string中,直到异步操作完成,然后解引用它以检查它的有效性。在上边的例子中你应该会观察到一个断言错误,就在Boost.Asio尝试调用完成处理器之前。
对于 Microsoft Visual Studio 8.0 或更新的版本,和定义了_GLIBCXX_DEBUG 的GCC,这个功能被自动设置为可用。这个检查会有性能消耗,因此缓冲区调试只启用在debug版本。对于其他编译器,可以通过定义BOOST_ASIO_ENABLE_BUFFER_DEBUGGING启用。也可以通过定义BOOST_ASIO_DISABLE_BUFFER_DEBUGGING显式关闭此功能。
最后
以上就是敏感柜子为你收集整理的Boost.Asio - Buffers的全部内容,希望文章能够帮你解决Boost.Asio - Buffers所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复