Search code examples
c++multithreadingboostboost-asio

Run tcp server in another thread


I'm struggling with running tcp server in different thread. So I have sth like that:

#include <ctime>
#include <iostream>
#include <string>

#include <boost/asio.hpp>

using boost::asio::ip::tcp;

std::string make_daytime_string()
{
    using namespace std; // For time_t, time and ctime;
    time_t now = time(0);
    return ctime(&now);
}

class TcpServer
{
private:
    boost::asio::io_context& io_context_;
    boost::asio::ip::tcp::acceptor acceptor_;
    boost::asio::ip::tcp::socket socket_;
    
public:
    TcpServer(boost::asio::io_context& io_context)
        : io_context_{io_context}
        , socket_(io_context_)
        , acceptor_(io_context_, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 512))
    {
        acceptor_.accept(socket_);
        std::string message = make_daytime_string();

        boost::system::error_code ignored_error;
        socket_.write_some(boost::asio::buffer(message), ignored_error);
    }
};


int main()
{
    boost::asio::io_context io_cont;
    std::thread t([&io_cont] ()
    {
        TcpServer server(io_cont);
    });

    std::cout << "I want to display it while server is still running";

    t.join();
    return 0;
}

Basically this TcpServer is running all the time until it gets connection, sends datetime and then exits. What I want to do is:

  1. Run the server
  2. Keep processing my things in main function
  3. Send command nc 127.0.0.1 512
  4. program ends

Solution

  • There's some confusion about using io_context; You seem to think operations run on the context, but they won't unless you use async_ versions.

    Other side notes:

    • write_some doesn't (need to) send the whole buffer. Use boost::asio::write instead.

    Here's a simpler example that clarifies the confusion:

    Live On Coliru

    #include <boost/asio.hpp>
    #include <ctime>
    #include <iostream>
    #include <string>
    using namespace std::chrono_literals;
    using boost::asio::ip::tcp;
    
    static std::string make_daytime_string()
    {
        using namespace std; // For time_t, time and ctime;
        time_t now = time(0);
        return ctime(&now);
    }
    
    class TcpServer {
      public:
        void run() {
            tcp::acceptor acceptor_(io_context_, {{}, 5120});
    
            boost::system::error_code ec;
            while (!ec) {
                auto socket_ = acceptor_.accept();
                std::string message = make_daytime_string();
                boost::asio::write(socket_, boost::asio::buffer(message), ec);
            }
        }
      private:
        boost::asio::io_context io_context_;
    };
    
    int main()
    {
        auto server = TcpServer{};
        std::thread t([&server]() { server.run(); });
    
        for (;; std::this_thread::sleep_for(1s))
            std::cout << "I want to display it while server is still running" << std::endl;
    
        t.join();
    }
    

    Prints

    ./a.out& for a in {1..9}; do sleep .5; nc 127.0.0.1 5120 <<< ''; done
    kill %1
    I want to display it while server is still running
    Mon May 24 17:41:35 2021
    I want to display it while server is still running
    Mon May 24 17:41:35 2021
    Mon May 24 17:41:36 2021
    I want to display it while server is still running
    Mon May 24 17:41:36 2021
    Mon May 24 17:41:37 2021
    I want to display it while server is still running
    Mon May 24 17:41:37 2021
    Mon May 24 17:41:38 2021
    I want to display it while server is still running
    Mon May 24 17:41:38 2021
    Mon May 24 17:41:39 2021