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;
}
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):
#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