天天看點

Boost.Asio使用入門

Boost.Asio使用入門

  1、概述:Boost.Asio是一個跨平台的C++庫,用于網絡和底層I/O程式設計,可以在I/O對象(如socket)上執行同步和異步操作。

  2、簡略的過程分析。以socket的連接配接操作為例:

  你的程式中需要至少定義一個io_service對象:boost::asio::io_service io_service。io_service表示程式到作業系統I/O服務的“連接配接”。

  為執行I/O操作,還需要一個I/O對象(通常需要使用io_service構造),如一個TCP套接字:boost::asio::ip::tcp::socket socket(io_service)。

  1)同步的連接配接過程中,發生以下事件序列(對應下面的左圖):

  (1)程式通過I/O對象啟動連接配接操作:socket.connect(server_endpoint);

  (2)I/O對象将請求轉發給io_service;

  (3)io_service請求作業系統去執行連接配接操作;

  (4)作業系統将操作結果傳回給io_service;

  (5)io_service将操作的(錯誤)結果轉換成boost::system::error_code對象,并回傳給I/O對象;

  (6)如果操作失敗,I/O對象抛出boost::system::system_error異常。

  如果是使用以下方式,則隻設定錯誤碼,不會抛出異常:

boost::system::error_code ec;     socket.connect(server_endpoint, ec);      

  2)異步的連接配接過程中,發生以下事件序列(對應下面的中圖和右圖):

  (1)程式通過I/O對象啟動連接配接操作:socket.async_connect(server_endpoint, your_completion_handler);

  your_completion_handler是一個函數(對象),原型:void your_completion_handler(const boost::system::error_code& ec);

  (3)io_service發信号給作業系統,告知它去開始一個異步的連接配接操作;

  一段時間過去... ...注意,在同步的情形下,程式會一直等待連接配接操作完成,而異步則是先立即傳回。

  (4)連接配接操作完成時,作業系統把結果放在隊列中;

  (5)程式必須調用io_service::run()(或類似函數)以取得操作結果。一般在你剛啟動第一個異步操作時就要調用run();

  io_service對象未停止(stopped()傳回false)且還有未完成的操作時,run()會一直阻塞,否則直接傳回。

  我的了解(io_service對象未停止時):如果目前有未完成的異步操作且隊列為空,則需要等待,是以run()将阻塞(在Linux下借助pstack可知是阻塞于epoll_wait()或pthread_cond_wait()等)。作業系統完成某個異步操作後,把結果放到隊列并通知應用程式。run()被“喚醒”,從隊列中取出結果并調用相應的回調函數;如果目前沒有未完成的異步操作且隊列為空,表示所有異步操作已經完成,則run()将直接傳回;當然,如果目前隊列非空,則run()直接取出結果并調用回調函數。

  asio保證了回調函數隻會被run()所線上程調用。是以,若沒有run(),回調函數永遠不會被調用。

  (6)在run()中io_service将操作結果取出隊列并翻譯成error_code,然後傳遞給your_completion_handler。

  

  3、例子:

// 一個簡單的回顯伺服器

#include <iostream>     #include <memory>     #include <array>     #include <boost/asio.hpp>     using boost::asio::ip::tcp;     // 伺服器和某個用戶端之間的“會話”     // 負責處理讀寫事件     class session : public std::enable_shared_from_this<session>     {     public:         session(tcp::socket s) : socket_(std::move(s)) {}         void start()         {             async_read();         }     private:         void async_read()         {             auto self(shared_from_this());             socket_.async_read_some(boost::asio::buffer(data_),  // 異步讀                 [this, self](const boost::system::error_code &ec, size_t bytes_transferred)  // 讀操作完成時回調該函數                 {  // 捕獲`self`使shared_ptr<session>的引用計數增加1,在該例中避免了async_read()退出時其引用計數變為0                     if (!ec)                         async_write(bytes_transferred);  // 讀完即寫                 }             );         }         void async_write(std::size_t length)         {             auto self(shared_from_this());             boost::asio::async_write(socket_, boost::asio::buffer(data_, length),  // 異步寫                 [this, self](const boost::system::error_code &ec, size_t)                 {                     if (!ec)                         async_read();                 }             );         }         tcp::socket socket_;  // “會話”基于已經建立的socket連接配接         std::array<char, 1024> data_;     };     // 伺服器類     // 監聽用戶端連接配接請求(async_accept)。與某個用戶端建立socket連接配接後,為它建立一個session     class server     {     public:         server(boost::asio::io_service &io_service, short port)             : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service)         {             async_accept();         }     private:         void async_accept()         {             acceptor_.async_accept(socket_, std::bind(&server::handle_accept, this, std::placeholders::_1));  // 異步accept。socket連接配接建立後,調用handle_accept()         }         void handle_accept(const boost::system::error_code &ec)         {             if (!ec)             {                 std::shared_ptr<session> session_ptr(new session(std::move(socket_)));                 session_ptr->start();             }             async_accept();  // 繼續監聽用戶端連接配接請求         }         tcp::acceptor acceptor_;         tcp::socket socket_;     };     int main(int argc, char* argv[])     {         boost::asio::io_service io_service;         server s(io_service, 52014);         io_service.run();         return 0;     }