官网上的例子在这里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> "; 25 std::cerr << " For IPv4, try: "; 26 std::cerr << " receiver 0.0.0.0 80 . "; 27 std::cerr << " For IPv6, try: "; 28 std::cerr << " receiver 0::0 80 . "; 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() << " "; 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更适合;
以上纯属个人学习笔记,如有理解不妥之处望高手指正;