異步操作
- 基本特徵
異步操作的特點就是,操作不會被阻塞,馬上返回,操作結果會通過回調函數告知。在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中的注意點
如上代碼,因爲調用的是異步IO接口,在傳入async_send_to的buffers,必須保證在IO過程中,它的內存是有效的,
怎麼在異步IO中保證內存的有效性也是一個難點,我們來看看asio中是如果通過asio buffer來保證異步IO的中數據內存的有效性。
asio buffer
- 基本概念
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包括指向數據的指針以及數據的大小,可以通過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);
- ConstBufferSequence和MutableBufferSequence
在asio定義了兩種模板類型分別是ConstBufferSequence和MutableBufferSequence,它們分別代表着const_buffer和mutable_buffer的集合。
ConstBufferSequence和MutableBufferSequence類型必須滿足如下要求:
- 首先它是一個sequence類型的容器
- 可析構和可複製
- 可迭代即有begine,end接口和迭代器類型
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)類型的
通過自定義的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接口即可。
注意:
- asio IO接口中的buffer都是屬於ConstBufferSequence或MutableBufferSequence
- 對於write操作使用的是ConstBufferSequence,對於read操作使用的是MutableBufferSequence