官網上的例子在這里https://www.boost.org/doc/libs/1_67_0/doc/html/boost_asio/examples/cpp03_examples.html
一 http::server 只有一個主線程
首先http::server是一個簡單的單線程服務器,只有一個主線程;
httpserver的思想比較簡單:主線程先預先申請一個連接對象connection並使用的acceptor對connection對象監聽客戶端的連接,連接到來后將該連接加入到連接管理connection_manager數組中,並重新預分配一個連接對象開始新一輪監聽;
connection_manager調用connection的start()函數,開始向io_context投遞接收請求,接收請求處理完后調用回調函數handle_read進行處理,並開始新一輪投遞接收請求;
里面比較重要的文件是
server.hpp server.cpp
connection.hpp connection.cpp
connection_manager.hpp connection_manager.cpp
main.cpp
其他文件在這三個server中是一樣的,用於處理接收和回復,這里不予討論。
connection作用:處理接收,發送請求
connection.hpp源碼如下:
1 // 2 // connection.hpp 3 // ~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #ifndef HTTP_CONNECTION_HPP 12 #define HTTP_CONNECTION_HPP 13 14 #include <boost/asio.hpp> 15 #include <boost/array.hpp> 16 #include <boost/noncopyable.hpp> 17 #include <boost/shared_ptr.hpp> 18 #include <boost/enable_shared_from_this.hpp> 19 #include "reply.hpp" 20 #include "request.hpp" 21 #include "request_handler.hpp" 22 #include "request_parser.hpp" 23 24 namespace http { 25 namespace server { 26 27 class connection_manager; 28 29 /// Represents a single connection from a client. 30 class connection 31 : public boost::enable_shared_from_this<connection>, 32 private boost::noncopyable 33 { 34 public: 35 /// Construct a connection with the given io_context. 36 explicit connection(boost::asio::io_context& io_context, 37 connection_manager& manager, request_handler& handler); 38 39 /// Get the socket associated with the connection. 40 boost::asio::ip::tcp::socket& socket(); 41 42 /// Start the first asynchronous operation for the connection. 43 void start(); 44 45 /// Stop all asynchronous operations associated with the connection. 46 void stop(); 47 48 private: 49 /// Handle completion of a read operation. 50 void handle_read(const boost::system::error_code& e, 51 std::size_t bytes_transferred); 52 53 /// Handle completion of a write operation. 54 void handle_write(const boost::system::error_code& e); 55 56 /// Socket for the connection. 57 boost::asio::ip::tcp::socket socket_; 58 59 /// The manager for this connection. 60 connection_manager& connection_manager_; 61 62 /// The handler used to process the incoming request. 63 request_handler& request_handler_; 64 65 /// Buffer for incoming data. 66 boost::array<char, 8192> buffer_; 67 68 /// The incoming request. 69 request request_; 70 71 /// The parser for the incoming request. 72 request_parser request_parser_; 73 74 /// The reply to be sent back to the client. 75 reply reply_; 76 }; 77 78 typedef boost::shared_ptr<connection> connection_ptr; 79 80 } // namespace server 81 } // namespace http 82 83 #endif // HTTP_CONNECTION_HPP
connection_manager& connection_manager_用來對當前所有的連接進行管理;
connection.cpp源碼如下:
1 // 2 // connection.cpp 3 // ~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #include "connection.hpp" 12 #include <vector> 13 #include <boost/bind.hpp> 14 #include "connection_manager.hpp" 15 #include "request_handler.hpp" 16 17 namespace http { 18 namespace server { 19 20 connection::connection(boost::asio::io_context& io_context, 21 connection_manager& manager, request_handler& handler) 22 : socket_(io_context), 23 connection_manager_(manager), 24 request_handler_(handler) 25 { 26 } 27 28 boost::asio::ip::tcp::socket& connection::socket() 29 { 30 return socket_; 31 } 32 33 void connection::start() 34 { 35 socket_.async_read_some(boost::asio::buffer(buffer_), 36 boost::bind(&connection::handle_read, shared_from_this(), 37 boost::asio::placeholders::error, 38 boost::asio::placeholders::bytes_transferred)); 39 } 40 41 void connection::stop() 42 { 43 socket_.close(); 44 } 45 46 void connection::handle_read(const boost::system::error_code& e, 47 std::size_t bytes_transferred) 48 { 49 if (!e) 50 { 51 boost::tribool result; 52 boost::tie(result, boost::tuples::ignore) = request_parser_.parse( 53 request_, buffer_.data(), buffer_.data() + bytes_transferred); 54 55 if (result) 56 { 57 request_handler_.handle_request(request_, reply_); 58 boost::asio::async_write(socket_, reply_.to_buffers(), 59 boost::bind(&connection::handle_write, shared_from_this(), 60 boost::asio::placeholders::error)); 61 } 62 else if (!result) 63 { 64 reply_ = reply::stock_reply(reply::bad_request); 65 boost::asio::async_write(socket_, reply_.to_buffers(), 66 boost::bind(&connection::handle_write, shared_from_this(), 67 boost::asio::placeholders::error)); 68 } 69 else 70 { 71 socket_.async_read_some(boost::asio::buffer(buffer_), 72 boost::bind(&connection::handle_read, shared_from_this(), 73 boost::asio::placeholders::error, 74 boost::asio::placeholders::bytes_transferred)); 75 } 76 } 77 else if (e != boost::asio::error::operation_aborted) 78 { 79 connection_manager_.stop(shared_from_this()); 80 } 81 } 82 83 void connection::handle_write(const boost::system::error_code& e) 84 { 85 if (!e) 86 { 87 // Initiate graceful connection closure. 88 boost::system::error_code ignored_ec; 89 socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); 90 } 91 92 if (e != boost::asio::error::operation_aborted) 93 { 94 connection_manager_.stop(shared_from_this()); 95 } 96 } 97 98 } // namespace server 99 } // namespace http
boost::bind用來產生一個函數對象,在本文中可以理解成使用boost::bind來注冊回調函數即可。
start()函數:
向io_context投遞接收數據請求,並注冊請求處理完后的回調函數handle_read()
handle_read()函數:
對接收到的數據進行分析,
如果成功或失敗都向客戶端發送一個答復,即向Io_context投遞一個發送請求,並注冊答復發送完后的回調處理函數handle_write();
如果分析結果為不確定,則繼續向io_context投遞一個接收請求;
handle_write()函數:
判斷是否向客戶端發送成功,如果成功后則優雅的關閉本次連接,
如果發送失敗且失敗碼不為operation_aborted則調用connection_manager來終止本次連接。
server類作用:綁定、監聽客戶端連接的到來
server.hpp源代碼如下:
1 // 2 // server.hpp 3 // ~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #ifndef HTTP_SERVER_HPP 12 #define HTTP_SERVER_HPP 13 14 #include <boost/asio.hpp> 15 #include <string> 16 #include <boost/noncopyable.hpp> 17 #include "connection.hpp" 18 #include "connection_manager.hpp" 19 #include "request_handler.hpp" 20 21 namespace http { 22 namespace server { 23 24 /// The top-level class of the HTTP server. 25 class server 26 : private boost::noncopyable 27 { 28 public: 29 /// Construct the server to listen on the specified TCP address and port, and 30 /// serve up files from the given directory. 31 explicit server(const std::string& address, const std::string& port, 32 const std::string& doc_root); 33 34 /// Run the server's io_context loop. 35 void run(); 36 37 private: 38 /// Initiate an asynchronous accept operation. 39 void start_accept(); 40 41 /// Handle completion of an asynchronous accept operation. 42 void handle_accept(const boost::system::error_code& e); 43 44 /// Handle a request to stop the server. 45 void handle_stop(); 46 47 /// The io_context used to perform asynchronous operations. 48 boost::asio::io_context io_context_; 49 50 /// The signal_set is used to register for process termination notifications. 51 boost::asio::signal_set signals_; 52 53 /// Acceptor used to listen for incoming connections. 54 boost::asio::ip::tcp::acceptor acceptor_; 55 56 /// The connection manager which owns all live connections. 57 connection_manager connection_manager_; 58 59 /// The next connection to be accepted. 60 connection_ptr new_connection_; 61 62 /// The handler for all incoming requests. 63 request_handler request_handler_; 64 }; 65 66 } // namespace server 67 } // namespace http 68 69 #endif // HTTP_SERVER_HPP
io_context_:用來執行整個程序的異步操作;
acceptor_:用來設置socket屬性,綁定和監聽;
server.cpp源代碼如下:
1 // 2 // server.cpp 3 // ~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #include "server.hpp" 12 #include <boost/bind.hpp> 13 #include <signal.h> 14 15 namespace http { 16 namespace server { 17 18 server::server(const std::string& address, const std::string& port, 19 const std::string& doc_root) 20 : io_context_(), 21 signals_(io_context_), 22 acceptor_(io_context_), 23 connection_manager_(), 24 new_connection_(), 25 request_handler_(doc_root) 26 { 27 // Register to handle the signals that indicate when the server should exit. 28 // It is safe to register for the same signal multiple times in a program, 29 // provided all registration for the specified signal is made through Asio. 30 signals_.add(SIGINT); 31 signals_.add(SIGTERM); 32 #if defined(SIGQUIT) 33 signals_.add(SIGQUIT); 34 #endif // defined(SIGQUIT) 35 signals_.async_wait(boost::bind(&server::handle_stop, this)); 36 37 // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR). 38 boost::asio::ip::tcp::resolver resolver(io_context_); 39 boost::asio::ip::tcp::endpoint endpoint = 40 *resolver.resolve(address, port).begin(); 41 acceptor_.open(endpoint.protocol()); 42 acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); 43 acceptor_.bind(endpoint); 44 acceptor_.listen(); 45 46 start_accept(); 47 } 48 49 void server::run() 50 { 51 // The io_context::run() call will block until all asynchronous operations 52 // have finished. While the server is running, there is always at least one 53 // asynchronous operation outstanding: the asynchronous accept call waiting 54 // for new incoming connections. 55 io_context_.run(); 56 } 57 58 void server::start_accept() 59 { 60 new_connection_.reset(new connection(io_context_, 61 connection_manager_, request_handler_)); 62 acceptor_.async_accept(new_connection_->socket(), 63 boost::bind(&server::handle_accept, this, 64 boost::asio::placeholders::error)); 65 } 66 67 void server::handle_accept(const boost::system::error_code& e) 68 { 69 // Check whether the server was stopped by a signal before this completion 70 // handler had a chance to run. 71 if (!acceptor_.is_open()) 72 { 73 return; 74 } 75 76 if (!e) 77 { 78 connection_manager_.start(new_connection_); 79 } 80 81 start_accept(); 82 } 83 84 void server::handle_stop() 85 { 86 // The server is stopped by cancelling all outstanding asynchronous 87 // operations. Once all operations have finished the io_context::run() call 88 // will exit. 89 acceptor_.close(); 90 connection_manager_.stop_all(); 91 } 92 93 } // namespace server 94 } // namespace http
connection_manager類用來管理所有的已連接對象;
connection_manager.hpp的源代碼如下:
1 // 2 // connection_manager.hpp 3 // ~~~~~~~~~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #ifndef HTTP_CONNECTION_MANAGER_HPP 12 #define HTTP_CONNECTION_MANAGER_HPP 13 14 #include <set> 15 #include <boost/noncopyable.hpp> 16 #include "connection.hpp" 17 18 namespace http { 19 namespace server { 20 21 /// Manages open connections so that they may be cleanly stopped when the server 22 /// needs to shut down. 23 class connection_manager 24 : private boost::noncopyable 25 { 26 public: 27 /// Add the specified connection to the manager and start it. 28 void start(connection_ptr c); 29 30 /// Stop the specified connection. 31 void stop(connection_ptr c); 32 33 /// Stop all connections. 34 void stop_all(); 35 36 private: 37 /// The managed connections. 38 std::set<connection_ptr> connections_; 39 }; 40 41 } // namespace server 42 } // namespace http 43 44 #endif // HTTP_CONNECTION_MANAGER_HPP
connection_manager.cpp源代碼如下:
1 // 2 // connection_manager.cpp 3 // ~~~~~~~~~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #include "connection_manager.hpp" 12 #include <algorithm> 13 #include <boost/bind.hpp> 14 15 namespace http { 16 namespace server { 17 18 void connection_manager::start(connection_ptr c) 19 { 20 connections_.insert(c); 21 c->start(); 22 } 23 24 void connection_manager::stop(connection_ptr c) 25 { 26 connections_.erase(c); 27 c->stop(); 28 } 29 30 void connection_manager::stop_all() 31 { 32 std::for_each(connections_.begin(), connections_.end(), 33 boost::bind(&connection::stop, _1)); 34 connections_.clear(); 35 } 36 37 } // namespace server 38 } // namespace http
最后在main.cpp中調用一個server對象開始服務的運行
main.cpp源代碼如下:
1 // 2 // main.cpp 3 // ~~~~~~~~ 4 // 5 // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #include <iostream> 12 #include <string> 13 #include <boost/asio.hpp> 14 #include <boost/bind.hpp> 15 #include "server.hpp" 16 17 int main(int argc, char* argv[]) 18 { 19 try 20 { 21 // Check command line arguments. 22 if (argc != 4) 23 { 24 std::cerr << "Usage: http_server <address> <port> <doc_root>\n"; 25 std::cerr << " For IPv4, try:\n"; 26 std::cerr << " receiver 0.0.0.0 80 .\n"; 27 std::cerr << " For IPv6, try:\n"; 28 std::cerr << " receiver 0::0 80 .\n"; 29 return 1; 30 } 31 32 // Initialise the server. 33 http::server::server s(argv[1], argv[2], argv[3]); 34 35 // Run the server until stopped. 36 s.run(); 37 } 38 catch (std::exception& e) 39 { 40 std::cerr << "exception: " << e.what() << "\n"; 41 } 42 43 return 0; 44 }
httpserver1比較簡單接下來我們看看Server2
二 http::server2 多個線程多個io_context
http server2的思想是每個線程分配一個io_context,讓每個線程都調用自己io_context::run函數,這樣可以保證每個線程訪問自己關聯的io_context的任務隊列。
http::server2::server相比較http::serve::server,去掉了connection_manager,增加了io_context_pool;
io_context_pool為每個線程分配一個io_context,
io_context_pool的源碼如下:
1 // 2 // io_context_pool.hpp 3 // ~~~~~~~~~~~~~~~~~~~ 4 // 5 // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) 6 // 7 // Distributed under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 9 // 10 11 #ifndef HTTP_SERVER2_IO_SERVICE_POOL_HPP 12 #define HTTP_SERVER2_IO_SERVICE_POOL_HPP 13 14 #include <boost/asio.hpp> 15 #include <list> 16 #include <vector> 17 #include <boost/noncopyable.hpp> 18 #include <boost/shared_ptr.hpp> 19 20 namespace http { 21 namespace server2 { 22 23 /// A pool of io_context objects. 24 class io_context_pool 25 : private boost::noncopyable 26 { 27 public: 28 /// Construct the io_context pool. 29 explicit io_context_pool(std::size_t pool_size); 30 31 /// Run all io_context objects in the pool. 32 void run(); 33 34 /// Stop all io_context objects in the pool. 35 void stop(); 36 37 /// Get an io_context to use. 38 boost::asio::io_context& get_io_context(); 39 40 private: 41 typedef boost::shared_ptr<boost::asio::io_context> io_context_ptr; 42 typedef boost::asio::executor_work_guard< 43 boost::asio::io_context::executor_type> io_context_work; 44 45 /// The pool of io_contexts. 46 std::vector<io_context_ptr> io_contexts_; 47 48 /// The work that keeps the io_contexts running. 49 std::list<io_context_work> work_; 50 51 /// The next io_context to use for a connection. 52 std::size_t next_io_context_; 53 }; 54 55 } // namespace server2 56 } // namespace http 57 58 #endif // HTTP_SERVER2_IO_SERVICE_POOL_HPP
// // io_context_pool.cpp // ~~~~~~~~~~~~~~~~~~~ // // Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #include "server.hpp" #include <stdexcept> #include <boost/thread/thread.hpp> #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> namespace http { namespace server2 { io_context_pool::io_context_pool(std::size_t pool_size) : next_io_context_(0) { if (pool_size == 0) throw std::runtime_error("io_context_pool size is 0"); // Give all the io_contexts work to do so that their run() functions will not // exit until they are explicitly stopped. for (std::size_t i = 0; i < pool_size; ++i) { io_context_ptr io_context(new boost::asio::io_context); io_contexts_.push_back(io_context); work_.push_back(boost::asio::make_work_guard(*io_context)); } } void io_context_pool::run() { // Create a pool of threads to run all of the io_contexts. std::vector<boost::shared_ptr<boost::thread> > threads; for (std::size_t i = 0; i < io_contexts_.size(); ++i) { boost::shared_ptr<boost::thread> thread(new boost::thread( boost::bind(&boost::asio::io_context::run, io_contexts_[i]))); threads.push_back(thread); } // Wait for all threads in the pool to exit. for (std::size_t i = 0; i < threads.size(); ++i) threads[i]->join(); } void io_context_pool::stop() { // Explicitly stop all io_contexts. for (std::size_t i = 0; i < io_contexts_.size(); ++i) io_contexts_[i]->stop(); } boost::asio::io_context& io_context_pool::get_io_context() { // Use a round-robin scheme to choose the next io_context to use. boost::asio::io_context& io_context = *io_contexts_[next_io_context_]; ++next_io_context_; if (next_io_context_ == io_contexts_.size()) next_io_context_ = 0; return io_context; } } // namespace server2 } // namespace http
在run()函數中為每個線程分配了一個io_context;每個線程調用各自的io_context::run函數(類似wait),
當這個io_context上有異步請求時就觸發在這個io_context上的run等待,然后去處理這個請求,等請求處理完畢后(比如接收數據完畢)調用請求傳入的回調函數進行下一步操作(多數時候是發起新一輪請求)。實際上run線程在等待到請求后,並不能立馬處理請求,它需要再次等待請求處理的條件的到來,比如,你發起了一個接收請求,如果這個時候客戶端並沒有向服務器發送數據,則這個接收請求就會在那里等待,直到客戶端有數據發送過來,然后run線程接收完數據再調用你傳入的回調函數,通知你請求處理完畢。
connection.hpp 和connection.cpp並無太大的變化,只是去掉了connection_manager相關代碼;
main.cpp中調用server對象時需要多傳入下參數:線程個數;
三 http::server3 一個io_context多個線程
http::server3的思想是:分配一個共享io_context,讓多個線程共同調用io_context::run(),即多個線程共同搶占Io_context任務隊列;
與http::server::server相比http::server3::server增加了:線程數 std::size_t thread_pool_size_,去掉了connection_manager;
核心代碼如下:
1 void server::run() 2 { 3 // Create a pool of threads to run all of the io_contexts. 4 std::vector<boost::shared_ptr<boost::thread> > threads; 5 for (std::size_t i = 0; i < thread_pool_size_; ++i) 6 { 7 boost::shared_ptr<boost::thread> thread(new boost::thread( 8 boost::bind(&boost::asio::io_context::run, &io_context_))); 9 threads.push_back(thread); 10 } 11 12 // Wait for all threads in the pool to exit. 13 for (std::size_t i = 0; i < threads.size(); ++i) 14 threads[i]->join(); 15 } 16 17 void server::start_accept() 18 { 19 new_connection_.reset(new connection(io_context_, request_handler_)); 20 acceptor_.async_accept(new_connection_->socket(), 21 boost::bind(&server::handle_accept, this, 22 boost::asio::placeholders::error)); 23 }
在run()函數中創建線程,並將每個線程與共享io_context進行綁定;
在start_accept()函數中預先分配一個連接對象,向共享io_context中投遞一個等待連接請求,有連接請求到來后調用handlle_accept進行處理,即主線程做監聽連接的活;
接下來看connection類
在connection類中增加了
boost::asio::io_context::strand strand_;來保證異步並發操作能正常有序進行,防止多個線程同時操作一個連接對象,說白了就是讓同一個連接上的回調函數執行串行化,所有異步回調操作的地方都需要使用strand_來進行控制
源碼如下:
#include "connection.h" #include <vector> #include <boost/bind.hpp> #include "connection_manager.h" #include "request_handler.h" namespace http { namespace server3 { connection::connection(boost::asio::io_context & io_context, request_handler & handler) :strand_(io_context), socket_(io_context), request_handler_(handler) { } boost::asio::ip::tcp::socket& connection::socket() { return socket_; } 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 ); } void connection::handle_read(const boost::system::error_code& e, std::size_t bytes_transferred) { if (!e) { boost::tribool result; boost::tie(result, boost::tuples::ignore) = request_parser_.parse( request_, buffer_.data(), buffer_.data() + bytes_transferred ); if (result) { request_handler_.handle_request(request_, reply_); boost::asio::async_write(socket_, reply_.to_buffers(), boost::asio::bind_executor(strand_, boost::bind(&connection::handle_write, shared_from_this(), boost::asio::placeholders::error)//end bin )//end bind_executor ); } else if (!result) { reply_ = reply::stock_reply(reply::bad_request); boost::asio::async_write(socket_, reply_.to_buffers(), boost::asio::bind_executor(strand_, boost::bind(&connection::handle_write, shared_from_this(), boost::asio::placeholders::error)//end bind )//end bind_executor ); } else { 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 ); } } //If an error occurs then no new asynchronous operations are started. This //means that all shared_ptr references to the connection object will //disappear and the object will be destroyed automatically after this //handler returns. The connection class's destructor closes the socket. } void connection::handle_write(const boost::system::error_code& e) { if (!e) { boost::system::error_code ignored_ec; socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); } // No new asynchronous operations are started. This means that all shared_ptr // references to the connection object will disappear and the object will be // destroyed automatically after this handler returns. The connection class's // destructor closes the socket. } }//namespace server3 }//namespace http
可以看出所有的async_操作都加上了strand_進行串行化控制;
四 結論
http::server沒什么好說的就是一個單線程,這里主要說明http::server2和http::server3的區別
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更適合;
以上純屬個人學習筆記,如有理解不妥之處望高手指正;
