Boost asio TCP async server not async?

前端 未结 2 684
礼貌的吻别
礼貌的吻别 2020-12-21 04:27

I am using the code provided in the Boost example.

The server only accepts 1 connection at a time. This means, no new connections until the current one is closed.

相关标签:
2条回答
  • 2020-12-21 05:07

    You're doing a synchronous wait inside the handler which runs on the only thread that serves your io_service. This makes Asio wait with invoking the handlers for any new requests.

    1. Use a deadline_time with wait_async, or,

      void do_read() {
          auto self(shared_from_this());
          socket_.async_read_some(boost::asio::buffer(data_, max_length),
                                  [this, self](boost::system::error_code ec, std::size_t length) {
              if (!ec) {
                  timer_.expires_from_now(boost::posix_time::seconds(1));
                  timer_.async_wait([this, self, length](boost::system::error_code ec) {
                          if (!ec)
                              do_write(length);
                      });
              }
          });
      }
      

      Where the timer_ field is a boost::asio::deadline_timer member of session

    2. as a poor-man's solution add more threads (this simply means that if more requests arrive at the same time than there are threads to handle them, it will still block until the first thread becomes available to pick up the new request)

      boost::thread_group tg;
      for (int i=0; i < 10; ++i)
          tg.create_thread([&]{ io_service.run(); });
      
      tg.join_all();
      
    0 讨论(0)
  • 2020-12-21 05:09

    Both the original code and the modified code are asynchronous and accept multiple connections. As can be seen in the following snippet, the async_accept operation's AcceptHandler initiates another async_accept operation, forming an asynchronous loop:

            .-----------------------------------.
            V                                   |
    void server::do_accept()                    |
    {                                           |
      acceptor_.async_accept(...,               |
          [this](boost::system::error_code ec)  |
          {                                     |
            // ...                              |
            do_accept();  ----------------------'
          });
    }
    

    The sleep() within the session's ReadHandler causes the one thread running the io_service to block until the sleep completes. Hence, the program will be doing nothing. However, this does not cause any outstanding operations to be cancelled. For a better understanding of asynchronous operations and io_service, consider reading this answer.


    Here is an example demonstrating the server handling multiple connections. It spawns off a thread that creates 5 client sockets and connects them to the server.

    #include <cstdlib>
    #include <iostream>
    #include <memory>
    #include <utility>
    #include <vector>
    #include <boost/asio.hpp>
    #include <boost/thread.hpp>
    
    using boost::asio::ip::tcp;
    
    class session
      : public std::enable_shared_from_this<session>
    {
    public:
      session(tcp::socket socket)
        : socket_(std::move(socket))
      {
      }
    
      ~session()
      {
        std::cout << "session ended" << std::endl;
      }
    
      void start()
      {
        std::cout << "session started" << std::endl;
        do_read();
      }
    
    private:
      void do_read()
      {
        auto self(shared_from_this());
        socket_.async_read_some(boost::asio::buffer(data_, max_length),
            [this, self](boost::system::error_code ec, std::size_t length)
            {
              if (!ec)
              {
                do_write(length);
              }
            });
      }
    
      void do_write(std::size_t length)
      {
        auto self(shared_from_this());
        boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
            [this, self](boost::system::error_code ec, std::size_t /*length*/)
            {
              if (!ec)
              {
                do_read();
              }
            });
      }
    
      tcp::socket socket_;
      enum { max_length = 1024 };
      char data_[max_length];
    };
    
    class server
    {
    public:
      server(boost::asio::io_service& io_service, short port)
        : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
          socket_(io_service)
      {
        do_accept();
      }
    
    private:
      void do_accept()
      {
        acceptor_.async_accept(socket_,
            [this](boost::system::error_code ec)
            {
              if (!ec)
              {
                std::make_shared<session>(std::move(socket_))->start();
              }
    
              do_accept();
            });
      }
    
      tcp::acceptor acceptor_;
      tcp::socket socket_;
    };
    
    int main(int argc, char* argv[])
    {
      try
      {
        if (argc != 2)
        {
          std::cerr << "Usage: async_tcp_echo_server <port>\n";
          return 1;
        }
    
        boost::asio::io_service io_service;
    
        auto port = std::atoi(argv[1]);
        server s(io_service, port);
    
        boost::thread client_main(
            [&io_service, port]
            {
              tcp::endpoint server_endpoint(
                  boost::asio::ip::address_v4::loopback(), port);
    
              // Create and connect 5 clients to the server.
              std::vector<std::shared_ptr<tcp::socket>> clients;
              for (auto i = 0; i < 5; ++i)
              {
                  auto client = std::make_shared<tcp::socket>(
                      std::ref(io_service));
                  client->connect(server_endpoint);
                  clients.push_back(client);
              }
    
              // Wait 2 seconds before destroying all clients.
              boost::this_thread::sleep(boost::posix_time::seconds(2));
            });
    
       io_service.run();
       client_main.join();
      }
      catch (std::exception& e)
      {
        std::cerr << "Exception: " << e.what() << "\n";
      }
    
      return 0;
    }
    

    The output:

    session started
    session started
    session started
    session started
    session started
    session ended
    session ended
    session ended
    session ended
    session ended
    
    0 讨论(0)
提交回复
热议问题