Just because a good sample will demonstrate that it won't help, here's your class-less echo server.
It's also:
- threadless
- async
- multi-session
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
using boost::asio::ip::tcp;
using boost::system::error_code;
using boost::asio::streambuf;
int main() {
boost::asio::io_service svc;
tcp::acceptor a(svc);
a.open(tcp::v4());
a.set_option(tcp::acceptor::reuse_address(true));
a.bind({{}, 6767}); // bind to port 6767 on localhost
a.listen(5);
using session = std::shared_ptr<tcp::socket>;
std::function<void()> do_accept;
std::function<void(session)> do_session;
do_session = [&](session s) {
// do a read
auto buf = std::make_shared<std::vector<char>>(1024);
s->async_read_some(boost::asio::buffer(*buf), [&,s,buf](error_code ec, size_t bytes) {
if (ec)
std::cerr << "read failed: " << ec.message() << "\n";
else {
std::cout << "Echo to " << s->remote_endpoint(ec) << ": " << std::string(buf->data(), bytes);
if (ec)
std::cerr << "endpoint failed: " << ec.message() << "\n";
else
async_write(*s, boost::asio::buffer(*buf), [&,s,buf](error_code ec, size_t) {
if (ec) std::cerr << "write failed: " << ec.message() << "\n";
});
do_session(s); // full duplex, can read while writing, using a second buffer
}
});
};
do_accept = [&] {
auto s = std::make_shared<session::element_type>(svc);
a.async_accept(*s, [&,s](error_code ec) {
if (ec)
std::cerr << "accept failed: " << ec.message() << "\n";
else {
do_session(s);
do_accept(); // accept the next
}
});
};
do_accept(); // kick-off
svc.run(); // wait for shutdown (Ctrl-C or failure)
}
BONUS
Because today's "let's use coroutines again" day, here's a version using boost::asio::spawn
:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <iostream>
namespace ba = boost::asio;
using ba::ip::tcp;
using ba::yield_context;
int main() {
ba::io_service svc;
tcp::acceptor a(svc);
a.open(tcp::v4());
a.set_option(tcp::acceptor::reuse_address(true));
a.bind({{}, 6767}); // bind to port 6767 on localhost
a.listen(5);
spawn(svc, [&a](yield_context yield) {
while(1) {
tcp::socket s(a.get_io_service());
a.async_accept(s, yield);
spawn(yield, [s=std::move(s)](yield_context yield) mutable {
// do a read
char buf[1024] = {};
size_t bytes = s.async_read_some(ba::buffer(buf), yield);
std::cout << "Echo to " << s.remote_endpoint() << ": " << std::string(buf, buf+bytes);
bytes = async_write(s, ba::buffer(buf), yield);
});
};
});
svc.run(); // wait for shutdown (Ctrl-C or failure)
}