异步操作的特点就是,操作不会被阻塞,马上返回,操作结果会通过回调函数告知。在asio中的异步IO也是如此,我们来看一个asio中udp socket的异步sendto接口,如下代码
dpSocket.async_send_to(buffers,
boost::asio::ip::udp::endpoint(destination.getAddress(), destination.getPort()),
boost::bind(&AsyncUdpSocketBase::handleSend, shared_from_this(), asio::placeholders::error)); //1
在udp socket上调用异步的sendto接口async_send_to,它会马上返回,IO结果会通过回调AsyncUdpSocketBase::handleSend告知
如上代码,因为调用的是异步IO接口,在传入async_send_to的buffers,必须保证在IO过程中,它的内存是有效的,
怎么在异步IO中保证内存的有效性也是一个难点,我们来看看asio中是如果通过asio buffer来保证异步IO的中数据内存的有效性。
asio 中的buffer并不申请内存,它只是一个内存块的封装,本质上就一个{void *data,size_t size}的一个数据对。可以理解为只是对内存的数据地址及大小的一个记录,方便asio异步接口对数据的使用。分为两种基本类型的对象:
boost::asio::const_buffer //不可变buffer
boost::asio::mutable_buffer //可变buffer
可以通过boost::asio::buffer接口来产生对应的类型的buffer对象
//通过std::string构造asio::buffer
std::string str = "test";
auto const_buffer = boost::asio::buffer(str.c_str(), str.size()); //1
//通过std::array构造asio::buffer
std::array<char,4> arryData = { 't','e','s','t' };
auto mutable_buffer = boost::asio::buffer(arryData);
//通过std::vector构造asio::buffer
std::vector<char> vecData = { 't','e','s','t' };
auto mutable_buffer = boost::asio::buffer(vecData);
注意语句1,因为std::string是const的,asio::buffer构造的为asio::const_buffer对象。
An individual buffer may be created from a builtin array, std::vector or boost::array of POD elements. This helps prevent buffer overruns by automatically determining the size of the buffer:
char d1[128];
size_t bytes_transferred = sock.receive(boost::asio::buffer(d1));
std::vector<char> d2(128);
bytes_transferred = sock.receive(boost::asio::buffer(d2));
boost::array<char, 128> d3;
bytes_transferred = sock.receive(boost::asio::buffer(d3));
如官方文档的描述,asio::buffer可以通过builtin array,std::vector or boost::array of POD element,还包括std::string。
asio::buffer包括指向数据的指针以及数据的大小,可以通过asio::buffer_cast访问数据指针,通过asio::buffer_size获取数据大小。
boost::asio::mutable_buffer b1;
std::size_t s1 = boost::asio::buffer_size(b1);
unsigned char* p1 = boost::asio::buffer_cast<unsigned char*>(b1);
boost::asio::const_buffer b2;
std::size_t s2 = boost::asio::buffer_size(b2);
const void* p2 = boost::asio::buffer_cast<const void*>(b2);
在asio定义了两种模板类型分别是ConstBufferSequence和MutableBufferSequence,它们分别代表着const_buffer和mutable_buffer的集合。
ConstBufferSequence和MutableBufferSequence类型必须满足如下要求:
asio中定义了 const_buffers_1和mutable_buffers_1两种类型,它们分别满足ConstBufferSequence和MutableBufferSequence的要求,通过asio::buffer接口可以产生const_buffers_1或mutable_buffers_1对象,具体可以查看boost asio buffer文档
相应的std::vectorasio::buffer,std::arrayasio::buffer,8也是属于ConstBufferSequence(MutableBufferSequence)类型的
在开头我们说到,异步IO的一个难点是需要保证内存的有效性,我们可以自定义一个ConstBufferSequence类型结合引用计数来保证内存的有效性。官方文档中有一个例子
class shared_const_buffer
{
public:
// Construct from a std::string.
explicit shared_const_buffer(const std::string& data)
: data_(new std::vector<char>(data.begin(), data.end())),
buffer_(boost::asio::buffer(*data_))
{
}
// Implement the ConstBufferSequence requirements.
typedef boost::asio::const_buffer value_type;
typedef const boost::asio::const_buffer* const_iterator;
const boost::asio::const_buffer* begin() const { return &buffer_; }
const boost::asio::const_buffer* end() const { return &buffer_ + 1; }
private:
boost::shared_ptr<std::vector<char> > data_;
boost::asio::const_buffer buffer_;
};
shared_const_buffer是一个ConstBufferSequence类型,注意成员变量data_是一个shared_ptr类型,其指向一个vector,代表了一块数据内存。buffer_是asio::const_buffer类型用于asio中的异步IO接口,我们在前面说过,asio::buffer只是一个内存块的封装,它并不拥有所有权。在这个例子中通过data_来管理内存,它是引用计数的。
void start()
{
//获取当前的时间
using namespace std;
time_t now = time(0);
//构造一个shared_const_buffer对象
shared_const_buffer buffer(ctime(&now));
//将shared_const_buffer用于async_write
boost::asio::async_write(socket_, buffer,boost::bind(&session::handle_write, shared_from_this()));
}
在start函数中,构造一个shared_const_buffer对象buffer用于asio的async_write接口,在IO接口中对buffer肯定是有拷贝操作的(此时buffer中的data_引用计数加一)成员变量data_的生命周期为整个async_write周期,即使start返回,整个async_write操作没完成前(结果回调执行完,才为整个异步IO完成),那么真正的内存数据data_都不会被释放,一直有效。
在这个例子中的shared_const_buffer只包含了一个asio::buffer,当然也可以定义一个asio::const_buffer的数组或std::vector< asio::const_buffer >,只需改动begin和end接口即可。
注意: