Boost asio是一個異步網絡通信的庫,其中async_write是一個比較常用的函數,但是,如果沒有正確的使用,就可能會出現一些意想不到的潛在Bug。例如下面的代碼:
- for (int i=0; i < n; i++)
- {
- boost::asio::async_write(
- socket_,
- boost::asio::buffer( buffer[i].data_.get(), buffer[i].length_ ),
- boost::asio::transfer_at_least(buffer[i].length_),
- boost::bind(
- &HttpServer::HandleTcpSend,
- shared_from_this(),
- boost::asio::placeholders::error,
- boost::asio::placeholders::bytes_transferred
- )
- );
- // Do something
- }
代碼很簡單,就是循環N次,發送N塊buffer。我們的目標是,接收端依次接收buffer1,buffer2,……,buffer n。但是,事實上,上面的代碼是有問題的,服務器可能會接收到完全錯亂的數據。先看一下正確的寫法,代碼如下:
- int i=0;
- boost::asio::async_write(
- socket_,
- boost::asio::buffer( buffer[i].data_.get(), buffer[i].length_ ),
- boost::asio::transfer_at_least(buffer[i].length_),
- boost::bind(
- &HttpServer::HandleTcpSend,
- shared_from_this(),
- boost::asio::placeholders::error,
- boost::asio::placeholders::bytes_transferred
- )
- );
- // Do something
- void HttpServer::HandleTcpSend(const boost::system::error_code& err,
- size_t bytes_transferred)
- {
- i++;
- boost::asio::async_write(
- socket_,
- boost::asio::buffer( buffer[i].data_.get(), buffer[i].length_ ),
- boost::asio::transfer_at_least(buffer[i].length_),
- boost::bind(
- &HttpServer::HandleTcpSend,
- shared_from_this(),
- boost::asio::placeholders::error,
- boost::asio::placeholders::bytes_transferred
- )
- );
- }
這個並不是asio的限制,底層套接字的一些函數,如send等也無法保證一次異步操作就把所有的數據都通過TCP流發送出去。async_write將被告知有多少字節實際發送了,然后要求在下一次異步操作時發送剩余的字節。async_write是通過一次或者多次調用async_write_some函數來實現的,那么如果在第一個async_write還沒有完成就調用第二個async_write,async_write_some就有可能先將第二個buffer的數據先發送出去。
因此,NEVER start your second async_write before the first has completed.