当前位置: 首页 > 工具软件 > Asio > 使用案例 >

asio中的buffer

田权
2023-12-01

异步操作

  1. 基本特征

异步操作的特点就是,操作不会被阻塞,马上返回,操作结果会通过回调函数告知。在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告知

  1. 异步IO中的注意点

如上代码,因为调用的是异步IO接口,在传入async_send_to的buffers,必须保证在IO过程中,它的内存是有效的,
怎么在异步IO中保证内存的有效性也是一个难点,我们来看看asio中是如果通过asio buffer来保证异步IO的中数据内存的有效性。

asio buffer

  1. 基本概念

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。

  1. 访问asio::buffer中的数据

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); 

  1. ConstBufferSequence和MutableBufferSequence

在asio定义了两种模板类型分别是ConstBufferSequence和MutableBufferSequence,它们分别代表着const_buffer和mutable_buffer的集合。

ConstBufferSequence和MutableBufferSequence类型必须满足如下要求:

  1. 首先它是一个sequence类型的容器
  2. 可析构和可复制
  3. 可迭代即有begine,end接口和迭代器类型

asio中定义了 const_buffers_1和mutable_buffers_1两种类型,它们分别满足ConstBufferSequence和MutableBufferSequence的要求,通过asio::buffer接口可以产生const_buffers_1或mutable_buffers_1对象,具体可以查看boost asio buffer文档

asio buffer

相应的std::vectorasio::buffer,std::arrayasio::buffer,8也是属于ConstBufferSequence(MutableBufferSequence)类型的

通过自定义的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接口即可。

注意:

  1. asio IO接口中的buffer都是属于ConstBufferSequence或MutableBufferSequence
  2. 对于write操作使用的是ConstBufferSequence,对于read操作使用的是MutableBufferSequence

asio文档中的例子

参考资料

 类似资料: