asio中的buffer

異步操作

  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文檔中的例子

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章