Boost庫系列:基於boost::asio的http、https serve實現方式總結

boost官網上的例子(https://www.boost.org/doc/libs/1_67_0/doc/html/boost_asio/examples/cpp03_examples.html)實現了四種httpserver的處理方式:

1、http::server,簡單的單線程服務器,只有一個主線程;

設計思想比較簡單:主線程先預先申請一個連接對象connection並使用的acceptor對connection對象監聽客戶端的連接,連接到來後將該連接加入到連接管理connection_manager數組中,並重新預分配一個連接對象開始新一輪監聽;

connection_manager調用connection的start()函數,開始向io_context投遞接收請求,接收請求處理完後調用回調函數handle_read進行處理,並開始新一輪投遞接收請求;

 

2、 http::server2  多個io_contex響應socket連接

基本原理:多個io_cotex對象可以同時存在,任何一個io_cotex對象只要調用io_contex::run()運行,它就能保證所有綁定在它身上的異步動作被檢測。所以你需要檢測的工作可以綁定在任意的io_cotex對象上,只要它在run()狀態即可。

思路:建立io_contex池,爲每一個io_contex對象開啓一個線程運行io_contex::run()。其中boost::asio::signal_set對象(響應unix信號異步處理)、boost::asio::ip::tcp::acceptor對象(接收器異步監聽連接的到來)綁定在固定一個io_cotex對象上,boost::asio::ip::tcp::socket對象則動態綁定到io_contex池中某個io_contex對象上,從而由io_contex池中多個io_contex對象共同分擔socket需要處理的工作量。

核心代碼:

//前置準備工作已經綁定信號處理signal_set對象、接收器對象acceptor_到固定的io_context對象(代碼略)
//前置工作準備妥當之後進入start_accept()函數。
void server::start_accept()
{
  new_connection_.reset(new connection(
        io_context_pool_.get_io_context(), //從io_context池中取下一個io_context對象出來進行綁定
        request_handler_));
  acceptor_.async_accept(new_connection_->socket(),//新綁定池中某個io_context對象的socket
      boost::bind(&server::handle_accept, this,
        boost::asio::placeholders::error));
}

void server::handle_accept(const boost::system::error_code& e)
{
  if (!e)
  {
    new_connection_->start(); //當前socket連接的異步處理
  }

  start_accept(); //重新將異步檢測註冊到io_context對象上,注io_context對象是接收器acceptor_所綁定的那個io_context對象。
}

3、 http::server3 一個io_context多個線程run()

http::server的單線程相比,http::server3主要是(1)將io_context::run()改由多線程中執行,每個線程run()實際上都是共享一個io_context對象。(2)定義boost::asio::io_contex::strand對象,由strand對象確保多線程run()執行的安全有序。

核心代碼:

void connection::start()
        {
            //使用strand來保證對該socket的訪問是串行化的
            socket_.async_read_some(boost::asio::buffer(buffer_),
                boost::asio::bind_executor(strand_, 
                    boost::bind(&connection::handle_read, 
                    shared_from_this(), 
                    boost::asio::placeholders::error, 
                    boost::asio::placeholders::bytes_transferred)//end bind
                )//end bind_executor
            );

        }

4、 http::server4 單線程的協程

5、http 總結

http::server沒什麼好說的就是一個單線程

http::server2

思想:爲每個線程分配一個io_context,每個線程訪問自己相關io_context的任務隊列。

優點:每個線程只訪問自己的任務隊列,不用增加額外的鎖相關開銷;且保證了一個socket連接只在一個線程中,不會出現兩個線程同時訪問該socket的情況
缺點:會出現一個線程忙死,另一個線程閒死的情況


http::server3

思想:分配一個共享io_context,讓多個線程共同調用io_context::run(),即多個線程共同搶佔Io_context任務隊列;

優點:每個線程的機會是均等的不會出現一個線程忙死,另一個線程閒死的情況;
缺點:多個線程訪問同一個任務隊列,增加額外加鎖,釋放鎖的開銷;並且因爲是多個線程訪問同一個任務隊列,就會出現兩個線程同時等待訪問一個socket的情況,

    要麼對該socket加鎖,要麼使用boost::strand來保證串行執行,不管用哪一個都增加額外開銷


通過比較發現在serve2中的優點恰是serve3的缺點,serve2的缺點恰是serve3的優點,具體使用哪個方案要看具體的項目,
如果是大量同時登錄且登錄後操作不多的情況server2更好一點,
如果是傳統應用中客戶端連接數比較少,且一個客戶端要對服務器做大量操作,則server3更適合;

6、https_server

https是http基礎上加入ssl加密協議,相較http方式,主要差異:

a、以 boost::asio::ssl::stream<boost::asio::ip::tcp::socket>  代替 boost::asio::ip::tcp::socket。

b、服務端accept成功之後, 需要socket().async_handshake(); 成功之後才能發起異步讀寫。如果是客戶端,也類似,客戶端connect成功之後, 需要socket().async_handshake(); 成功之後才能發起異步讀寫。

與tcp socket相比, 多一個handshake過程。 數據的封包與解析與tcp完全一致,無需另行處理ssl協議數據,連接生成的socket對象已經自動在內部實現加解密。

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