Echo Server with Corosio
A complete echo server using Corosio for real network I/O.
What You Will Learn
-
Integrating Capy with Corosio networking
-
Accepting TCP connections with
tcp_acceptor -
Handling multiple clients concurrently
Prerequisites
-
Completed Custom Dynamic Buffer
-
Corosio library installed
-
Understanding of TCP networking basics
Source Code
#include <boost/capy.hpp>
#include <boost/corosio.hpp>
#include <iostream>
namespace corosio = boost::corosio;
using namespace boost::capy;
task<> echo_session(corosio::tcp_socket sock)
{
char buf[1024];
for (;;)
{
auto [ec, n] = co_await sock.read_some(
mutable_buffer(buf, sizeof(buf)));
if (ec)
break;
auto [wec, wn] = co_await write(
sock, const_buffer(buf, n));
if (wec)
break;
}
sock.close();
}
task<> accept_loop(
corosio::tcp_acceptor& acc,
corosio::io_context& ioc)
{
auto ep = acc.local_endpoint();
std::cout << "Listening on port " << ep.port() << "\n";
for (;;)
{
corosio::tcp_socket peer(ioc);
auto [ec] = co_await acc.accept(peer);
if (ec)
{
std::cout << "Accept error: " << ec.message() << "\n";
continue;
}
auto remote = peer.remote_endpoint();
std::cout << "Connection from ";
if (remote.is_v4())
std::cout << remote.v4_address();
else
std::cout << remote.v6_address();
std::cout << ":" << remote.port() << "\n";
run_async(ioc.get_executor())(
echo_session(std::move(peer)));
}
}
int main(int argc, char* argv[])
{
unsigned short port = 8080;
if (argc > 1)
port = static_cast<unsigned short>(std::atoi(argv[1]));
corosio::io_context ioc;
corosio::tcp_acceptor acc(ioc, corosio::endpoint(port));
run_async(ioc.get_executor())(
accept_loop(acc, ioc));
ioc.run();
return 0;
}
Build
add_executable(echo_server echo_server.cpp)
target_link_libraries(echo_server PRIVATE Boost::capy Boost::corosio)
Walkthrough
TCP Acceptor
corosio::io_context ioc;
corosio::tcp_acceptor acc(ioc, corosio::endpoint(port));
The io_context drives all asynchronous I/O. The tcp_acceptor listens on the specified port. Corosio uses a flat namespace — types like tcp_socket, tcp_acceptor, and endpoint live directly in boost::corosio.
Accept Loop
for (;;)
{
corosio::tcp_socket peer(ioc);
auto [ec] = co_await acc.accept(peer);
// ... handle connection ...
}
The accept loop runs forever, creating a new tcp_socket for each connection. acc.accept(peer) suspends the coroutine until a client connects.
Echo Session
auto [ec, n] = co_await sock.read_some(
mutable_buffer(buf, sizeof(buf)));
// ...
auto [wec, wn] = co_await write(
sock, const_buffer(buf, n));
Each session reads data with read_some and writes it back with write. When the client disconnects, read_some returns an error and the loop exits.
Testing
Start the server:
$ ./echo_server 8080
Listening on port 8080
Connect with netcat:
$ nc localhost 8080
Hello
Hello
World
World
^C
Server output:
Listening on port 8080
Connection from 127.0.0.1:54321
Exercises
-
Add a connection limit with graceful rejection
-
Implement a simple command protocol (e.g., ECHO, QUIT, STATS)
-
Add TLS support using Corosio’s TLS streams
Next Steps
-
Stream Pipeline — Data transformation chains