Search code examples
c++socketsboosttcpasio

boost asio socket with only ONE accepted message


I'm new in coding boost asio and asynchronous TCP socket data transfer.

I've been stopped in sending multiple messages from client to server because my server accepts only one message. I tried to follow solution here to call another async_read_some in my handler_read method but still cannot get second message from a client to get accepted by server.

I provided some server side code here. Of course some unnecessary parts have been removed.

class ConnectionHandler : public boost::enable_shared_from_this<ConnectionHandler> {
private:
tcp::socket _Socket;
string      _Message = "Message From Server!\n";
char        _Data[1024];

public:
typedef boost::shared_ptr<ConnectionHandler> pointer;

ConnectionHandler(boost::asio::io_service& io_service)
:
_Socket { io_service }, _Data { 0 }
{}

static pointer Create(boost::asio::io_service& io_service)
{
    return pointer(new ConnectionHandler(io_service));
}

tcp::socket& Socket()
{
    return _Socket;
}

void Start()
{
    _Socket.async_read_some(boost::asio::buffer(_Data, 1024),boost::bind(&ConnectionHandler::handle_read,shared_from_this(),boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));

    _Socket.async_write_some(boost::asio::buffer(_Message, 1024),boost::bind(&ConnectionHandler::handle_write,shared_from_this(),boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}

void handle_read(const boost::system::error_code& error, std::size_t bytes_transferred)
{
    if (!error)
    {
        cout << _Data << endl;
       _Socket.async_read_some(boost::asio::buffer(_Data, 1024),boost::bind(&ConnectionHandler::handle_read,shared_from_this(),boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
    }
}

class Server {
private:
tcp::acceptor Acceptor;
void StartAccept()
{
    // socket
    ConnectionHandler::pointer connection = ConnectionHandler::Create((boost::asio::io_context&)(Acceptor).get_executor().context());

    // asynchronous accept operation

    Acceptor.async_accept(connection->Socket(), boost::bind(&Server::handle_accept, this, connection, boost::asio::placeholders::error));
}

public:
// constructor for accepting connection from client
 Server(boost::asio::io_service& io_service)
:
Acceptor{ io_service, tcp::endpoint(tcp::v4(), 1234) }
{
    StartAccept();
}

void handle_accept(ConnectionHandler::pointer connection, const boost::system::error_code& error)
{
    if (!error)
        connection->Start();
    StartAccept();
}
};

int main()
{
    try
    {
        boost::asio::io_service io_service;

        Server server(io_service);

        io_service.run();
    }
    catch(exception& e)
    {
        cerr << e.what() << endl;
    }

    return 0;
}

Solution

  • First off, let's get the code into the last decade.

    io_service has been deprecated for years, std::bind and std::shared_ptr exist etc.

    Sprinkle in some const-correctness, no using-namespace abuse, not using C-style casts, not using C-arrays we get - hold on, also stop manually specifying the wrong buffer length (1024 makes no sense with the message):

    Live On Coliru

    #include <boost/asio.hpp>
    #include <iostream>
    
    namespace asio = boost::asio;
    using namespace std::placeholders;
    using asio::ip::tcp;
    using boost::system::error_code;
    
    class ConnectionHandler : public std::enable_shared_from_this<ConnectionHandler> {
      private:
        tcp::socket            sock_;
        std::string            msg_ = "Message From Server!\n";
        std::array<char, 1024> data_;
    
      public:
        typedef std::shared_ptr<ConnectionHandler> pointer;
    
        ConnectionHandler(asio::any_io_executor ex) : sock_{ex}, data_{0} {}
    
        static pointer Create(asio::any_io_executor ex) { return pointer(new ConnectionHandler(ex)); }
    
        tcp::socket& getSocket() { return sock_; }
    
        template <typename PMF> auto callback(PMF pmf) { return std::bind(pmf, shared_from_this(), _1, _2); }
    
        void Start() {
            sock_.async_read_some(asio::buffer(data_), callback(&ConnectionHandler::handle_read));
            sock_.async_write_some(asio::buffer(msg_), callback(&ConnectionHandler::handle_write));
        }
    
        void handle_read(error_code error, size_t bytes_transferred) {
            if (!error) {
                std::cout << std::string_view(data_.data(), bytes_transferred) << std::endl;
                sock_.async_read_some(asio::buffer(data_), callback(&ConnectionHandler::handle_read));
            }
        }
    
        void handle_write(error_code, size_t) {}
    };
    
    class Server {
      private:
        tcp::acceptor acceptor_;
    
        void StartAccept() {
            auto conn = ConnectionHandler::Create(acceptor_.get_executor());
            acceptor_.async_accept(conn->getSocket(), std::bind(&Server::handle_accept, this, conn, _1));
        }
    
      public:
        // constructor for accepting connection from client
        Server(asio::any_io_executor ex) : acceptor_{ex, {{}, 1234}} { StartAccept(); }
    
        void handle_accept(ConnectionHandler::pointer connection, error_code error) {
            if (!error)
                connection->Start();
            StartAccept();
        }
    };
    
    int main() try {
        asio::io_context ioc;
        Server           server(ioc.get_executor());
    
        ioc.run_for(std::chrono::seconds(10)); // for COLIRU
        // ioc.run();
    } catch (std::exception const& e) {
        std::cerr << e.what() << std::endl;
    }
    

    Now certainly it accepts multiple connections:

    g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp
    ./a.out&
    for a in {1..5}; do (sleep 1.${RANDOM}; echo Hello world | nc 127.0.0.1 1234 -w2)& done
    wait
    

    Prints

    Message From Server!
    Hello world
    
    Message From Server!
    Hello world
    
    Message From Server!
    Hello world
    
    Message From Server!
    Hello world
    
    Message From Server!
    Hello world