Search code examples
boostboost-asio

How to start Boost echo server in a separate thread


I was using boost version boost 1_64_0 to run a Tcp server in a seprate thread

This was the code for the server.

https://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp

in the Main loop i could spawn a new thread like this.

int main()
{

// Run the boost echo server as a different thread
boost::asio::io_service io_service;
server server1(io_service, 1980);   
boost::thread t(boost::bind(&io_service::run, &io_service));    

// when about to close the program
io_service.stop(); // stop the server
t.join();
}

Now i have changed the boost version to boost_1_73_0 and i am using this example to create a server

https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp

how do to create a new thread ?

The existing code to create a new thread gives error.

  1. io_service: left of :: must be a class/struct/union

  2. run : undeclared identifier


Solution

  • In &io_service::run, io_service is not a type but the local variable by that name.

    Starting from https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp

        io_context.run();
    

    Needs to be like

        std::thread t([&] { io_context.run(); });
        // ...
        t.join();
    

    Or if you insist:

        std::thread t(boost::bind(&boost::asio::io_context::run, &io_context));
    

    I would use a thread_pool to shortcut all the complexities¹:

        boost::asio::thread_pool io(1);
    
        server s(io.get_executor(), std::atoi(argv[1]));
    
        // ...
        io.join();
    

    A trivial edit to the class interface is required (see e.g. Boost 1.70 io_service deprecation)

    Live On Coliru

    //
    // async_tcp_echo_server.cpp
    // ~~~~~~~~~~~~~~~~~~~~~~~~~
    //
    // Copyright (c) 2003-2020 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 <boost/asio.hpp>
    #include <boost/bind/bind.hpp>
    #include <cstdlib>
    #include <iostream>
    #include <memory>
    #include <utility>
    
    using boost::asio::ip::tcp;
    
    class session : public std::enable_shared_from_this<session> {
      public:
        session(tcp::socket socket) : socket_(std::move(socket)) {}
    
        void start() { 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:
        template <typename Executor>
        server(Executor ex, short port)
                : acceptor_(ex, tcp::endpoint(tcp::v4(), port)) {
            do_accept();
        }
    
      private:
        void do_accept() {
            acceptor_.async_accept(
                [this](boost::system::error_code ec, tcp::socket socket) {
                    if (!ec) {
                        std::make_shared<session>(std::move(socket))->start();
                    }
    
                    do_accept();
                });
        }
    
        tcp::acceptor acceptor_;
    };
    
    int main(int argc, char* argv[]) {
        try {
            if (argc != 2) {
                std::cerr << "Usage: async_tcp_echo_server <port>\n";
                return 1;
            }
    
            boost::asio::thread_pool io(1);
    
            server s(io.get_executor(), std::atoi(argv[1]));
    
            // ...
            io.join();
        } catch (std::exception& e) {
            std::cerr << "Exception: " << e.what() << "\n";
        }
    }
    

    ¹ see e.g. Should the exception thrown by boost::asio::io_service::run() be caught?