缓冲对象
执行从连续内存中转发数据的IO行为,我们称其为缓冲Buffer。这种缓冲可以被表达为一个指针和一个长度变量的区间。然而,要想完成一个高效的网络模型,支持scatter-father操作,我们的操作过程中需要包含一个或者多个缓冲区:
- scatter-read离散地读取数据到多元的缓冲区中。
- gather-write聚合写入转发多元缓冲对象。
有鉴于此,我们需要一种对缓冲集合的抽象,在Asio中使用的方法是定义了一种类型表示单一缓冲对象,这种对象可以存放到特定容器中,然后通过这些容器我们可以进行scatter-gather操作。
为了让buffer类型具有类似于指针缓冲的特性,我们又将易变内存和常量内存区别对待,这两者可以被如下定义:
typedef std::pair<void*, std::size_t> mutable_buffer;
typedef std::pair<const void*,std::size_t> const_buffer;buffer_end
和普通常量相同,易变缓冲能转化为常缓冲,反之则非法。
但实际上,Asio并没有用如上的定义,而是定义了两种类型mutable_buffer和const_buffer。这样设计的原因是为了对连续内存有一个模糊的定义:
- std::pair对于不支持常量变换,我们的类型允许两者在一定规则下进行变换
- Asio存在机制来避免buffer的滥用。声明一个缓冲的实例,你只能再创建一个相同内存块或者子内存块的缓冲。为了保证其安全,库中包含了机制来自动推断一个来自于数组的缓冲区大小,比如boost::array,std::vector或者std::string。
- 类型安全的保证是在进行类型转化的过程中,你只能使用buffer_cast。一般来说,应用程序不应该调用该函数,但是库内部需要调用它来实现对操作系统底层的内存交付。
最后,多元缓冲能够执行scatter-gather操作(比如使用read和write函数)将缓冲对象存入容器中。MutableBufferSequence和ConstBufferSequence的概念允许我们使用诸如std::vector,std::list或者boost::array的容器(译者注:此处作者意味我们可以使用储存结构为std::vector或std::list的容器对象,比如std::set<T, cmp, vector<T>>)
通过iostream实现一体化流式缓冲
asio::basic_streambuf类是std::basic_streambuf的子类,用以处理包含存放随机类型数组的输入输出序列。这些数组对象包含在流式缓冲中,但是直接操作流中的数组是非法的,你只能通过IO操作访问他们,例如socket的send和receive:
- 输入序列通过data()成员函数被允许访问,而且返回的类型正好符合ConstBufferSequence的需求。
- 输出序列通过prepare()成员函数被允许访问,返回的类型正好符合MutableBufferSequence。
- 当我们要从输出序列队头向输入序列队尾转发数据时,我们调用commit()成员函数。
- 当我们要从输入序列队头移除数据,我们调用consume()。
strambuf的构造器接受一个size_t类型的变量来确定输入输出序列的最大长度,任何试图越界的操作都会抛出std::length_error的异常。
缓冲序列的迭代访问
buffer_iterator<>类提供了方法访问连续的内存。辅助的函数buffer_begin()和buffer_end(),迭代器的模板类型是自动推断的。
在下面的例子中,我们从socket中读取一个单行,然后写入std::string中:
aiso::streambuf sb;
...
std::size_t n = asio::read_unit(sock, sb, '/n');
asio::streambuf::const_buffers_type bufs = sb.data();
std::string line(
asio::buffers_begin(bufs),
asio::buffers_begin(bufs) + n);
Buffers Debugging
有的标准库的实现提供了一种feature叫做iterator debugging,比如MS VC++ 8.0。这种特征意味着在运行时会检测迭代器的合法性,使用非法迭代器会造成断言:
void dont_do_this()
{
std::string msg = "Hello, world!";
asio::async_write(sock, asio::buffer(msg), my_handler);
}
当你调用异步读写的时候,你要确认你的缓冲对象直到你的回调发生为止,必须要是合法的。在上述的例子中,缓冲对象中存放的是标准字符串对象,该对象在栈中,在出函数块后被系统回收了,如果你足够幸运,你的应用会崩溃,更多的可能是发生无法预见的随机问题。
当buffer debugging被开启,Asio会自动存放一个迭代器给那个字符串对象,直到异步操作完成为止,然后间接地检查其合法性。在调用异步回调之前,断言就会被触发。
这个特性在MS VS 8.0和之后的版本自动实现,GCC需要开启_GLIB_CXX_DEBUG指令。由于有额外开销,所以该特性只会在debug模式下有用。如果你是其他的C++编译器,可以在Asio中开启 ASIO_ENABLE_BUFFER_DEBUGGING宏,使用 ASIO_DISAB LE_BUFFER_DEBUGGING来禁用该特性。
参见
buffer, buffers_begin, buffers_end, buffers_iterator, const_buffer, const_buffers_1, mutable_buffer, mutable_buffers_1, streambuf, ConstBufferSequence, MutableBufferSequence, buffers example (C++03), buffers example (c++11).