1

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;
}
  • Are you sure your client is sending multiple messages? Have you checked with Wireshark? Have you tried printing the error messages or using a debugger? – Alan Birtles Jul 09 '23 at 12:28
  • @AlanBirtles if I remove the ```async_read_some``` inside ```handle_read```, second, third, ... messages, all fail to get written from client side. In case I leave ```async_read_some``` inside, after first message written successfully in client, it hangs forever. – Iman Abdollahzadeh Jul 09 '23 at 12:56
  • So the problem is with the client? Isn't this the server code? – Alan Birtles Jul 09 '23 at 14:32
  • Are you using a book? If so, which book are you using for the example code – sehe Jul 09 '23 at 14:48

1 Answers1

1

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
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Really thanks for detailed answer. I will give it a try with my code and in case of any queries, I will post my client side code here as well. I suspect that client side has bugous implementation. – Iman Abdollahzadeh Jul 09 '23 at 16:41