I am using the code provided in the Boost example.
The server only accepts 1 connection at a time. This means, no new connections until the current one is closed.
You're doing a synchronous wait inside the handler which runs on the only thread that serves your io_service. This makes Asio wait with invoking the handlers for any new requests.
Use a deadline_time
with wait_async
, or,
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) {
timer_.expires_from_now(boost::posix_time::seconds(1));
timer_.async_wait([this, self, length](boost::system::error_code ec) {
if (!ec)
do_write(length);
});
}
});
}
Where the timer_
field is a boost::asio::deadline_timer
member of session
as a poor-man's solution add more threads (this simply means that if more requests arrive at the same time than there are threads to handle them, it will still block until the first thread becomes available to pick up the new request)
boost::thread_group tg;
for (int i=0; i < 10; ++i)
tg.create_thread([&]{ io_service.run(); });
tg.join_all();
Both the original code and the modified code are asynchronous and accept multiple connections. As can be seen in the following snippet, the async_accept
operation's AcceptHandler initiates another async_accept
operation, forming an asynchronous loop:
.-----------------------------------.
V |
void server::do_accept() |
{ |
acceptor_.async_accept(..., |
[this](boost::system::error_code ec) |
{ |
// ... |
do_accept(); ----------------------'
});
}
The sleep()
within the session
's ReadHandler causes the one thread running the io_service
to block until the sleep completes. Hence, the program will be doing nothing. However, this does not cause any outstanding operations to be cancelled. For a better understanding of asynchronous operations and io_service
, consider reading this answer.
Here is an example demonstrating the server handling multiple connections. It spawns off a thread that creates 5 client sockets and connects them to the server.
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using boost::asio::ip::tcp;
class session
: public std::enable_shared_from_this<session>
{
public:
session(tcp::socket socket)
: socket_(std::move(socket))
{
}
~session()
{
std::cout << "session ended" << std::endl;
}
void start()
{
std::cout << "session started" << std::endl;
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:
server(boost::asio::io_service& io_service, short port)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
socket_(io_service)
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(socket_,
[this](boost::system::error_code ec)
{
if (!ec)
{
std::make_shared<session>(std::move(socket_))->start();
}
do_accept();
});
}
tcp::acceptor acceptor_;
tcp::socket socket_;
};
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::io_service io_service;
auto port = std::atoi(argv[1]);
server s(io_service, port);
boost::thread client_main(
[&io_service, port]
{
tcp::endpoint server_endpoint(
boost::asio::ip::address_v4::loopback(), port);
// Create and connect 5 clients to the server.
std::vector<std::shared_ptr<tcp::socket>> clients;
for (auto i = 0; i < 5; ++i)
{
auto client = std::make_shared<tcp::socket>(
std::ref(io_service));
client->connect(server_endpoint);
clients.push_back(client);
}
// Wait 2 seconds before destroying all clients.
boost::this_thread::sleep(boost::posix_time::seconds(2));
});
io_service.run();
client_main.join();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
The output:
session started
session started
session started
session started
session started
session ended
session ended
session ended
session ended
session ended