• boost::asio httpserve httpserver2 httpserver3的比较


    官网上的例子在这里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
    View Code
    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
    View Code

    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
    View Code
    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
    View Code

    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
    View Code

    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
    View Code

    最后在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 }
    View Code

    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更适合;


    以上纯属个人学习笔记,如有理解不妥之处望高手指正;







  • 相关阅读:
    logback学习二
    logback学习
    弱类型、强类型、动态类型、静态类型语言的区别
    BlockingQueue
    ExecutorService
    Future学习
    SetTimeout()多次运行函数后越来越快的问题
    LISTAGG函数
    Oracle字段
    使用powerdesigner进行数据库设计
  • 原文地址:https://www.cnblogs.com/guoliushui/p/9055603.html
Copyright © 2020-2023  润新知