Echo Client/Server
This page demonstrates simple echo client and server examples using the asynchronous I/O and networking library.
For more details and the full source code, see:
Echo Client Example
The following is a simplified echo client program. It reads lines from standard input, connects to an echo server, sends the data, and then prints the echoed response.
#include <coroio/all.hpp>
#include <signal.h>
#include <cstring>
#include <cstdlib>
#include <iostream>
using namespace NNet;
template<bool debug, typename TPoller>
TFuture<void> client(TPoller& poller, TAddress addr)
{
static constexpr int maxLineSize = 4096;
using TSocket = typename TPoller::TSocket;
using TFileHandle = typename TPoller::TFileHandle;
std::vector<char> in(maxLineSize);
try {
TFileHandle input{0, poller}; // stdin
TSocket socket{poller, addr.Domain()};
TLineReader lineReader(input, maxLineSize);
TByteWriter byteWriter(socket);
TByteReader byteReader(socket);
// Windows requires a timeout to detect 'connection refused'
co_await socket.Connect(addr, TClock::now() + std::chrono::milliseconds(1000));
while (auto line = co_await lineReader.Read()) {
co_await byteWriter.Write(line);
co_await byteReader.Read(in.data(), line.Size());
if constexpr(debug) {
std::cout << "Received: " << std::string_view(in.data(), line.Size()) << "\n";
}
}
} catch (const std::exception& ex) {
std::cout << "Exception: " << ex.what() << "\n";
}
co_return;
}
template<typename TPoller>
void run(bool debug, TAddress address)
{
TLoop<TPoller> loop;
TFuture<void> h;
if (debug) {
h = client<true>(loop.Poller(), std::move(address));
} else {
h = client<false>(loop.Poller(), std::move(address));
}
while (!h.done()) {
loop.Step();
}
}
void usage(const char* name) {
std::cerr << name << " [--port 80] [--addr 127.0.0.1] [--method select|poll|epoll|kqueue] [--debug] [--help]" << std::endl;
std::exit(1);
}
int main(int argc, char** argv) {
TInitializer init;
std::string addr = "127.0.0.1";
int port = 0;
std::string method = "select";
bool debug = false;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--port") && i < argc-1) {
port = atoi(argv[++i]);
} else if (!strcmp(argv[i], "--addr") && i < argc-1) {
addr = argv[++i];
} else if (!strcmp(argv[i], "--method") && i < argc-1) {
method = argv[++i];
} else if (!strcmp(argv[i], "--debug")) {
debug = true;
} else if (!strcmp(argv[i], "--help")) {
usage(argv[0]);
}
}
if (port == 0) { port = 8888; }
TAddress address{addr, port};
std::cerr << "Method: " << method << "\n";
if (method == "select") {
run<TSelect>(debug, address);
}
else if (method == "poll") {
run<TPoll>(debug, address);
}
#ifdef HAVE_EPOLL
else if (method == "epoll") {
run<TEPoll>(debug, address);
}
#endif
#ifdef HAVE_URING
else if (method == "uring") {
run<TUring>(debug, address);
}
#endif
#ifdef HAVE_KQUEUE
else if (method == "kqueue") {
run<TKqueue>(debug, address);
}
#endif
#ifdef HAVE_IOCP
else if (method == "iocp") {
run<TIOCp>(debug, address);
}
#endif
else {
std::cerr << "Unknown method\n";
}
return 0;
}
Echo Server Example
Below is a simplified echo server program. It listens on a specified port, accepts incoming connections, and for each client, echoes back any data it receives.
#include <coroio/all.hpp>
using NNet::TVoidTask;
using NNet::TAddress;
using NNet::TSelect;
using NNet::TPoll;
#ifdef HAVE_EPOLL
using NNet::TEPoll;
#endif
#ifdef HAVE_URING
using NNet::TUring;
#endif
#ifdef HAVE_KQUEUE
using NNet::TKqueue;
#endif
#ifdef HAVE_IOCP
using NNet::TIOCp;
#endif
template<bool debug, typename TSocket>
TVoidTask client_handler(TSocket socket, int buffer_size) {
std::vector<char> buffer(buffer_size);
ssize_t size = 0;
try {
while ((size = co_await socket.ReadSome(buffer.data(), buffer_size)) > 0) {
if constexpr(debug) {
std::cerr << "Received: " << std::string_view(buffer.data(), size) << "\n";
}
co_await socket.WriteSome(buffer.data(), size);
}
} catch (const std::exception& ex) {
std::cerr << "Exception: " << ex.what() << "\n";
}
if (size == 0) {
std::cerr << "Client disconnected\n";
}
co_return;
}
template<bool debug, typename TPoller>
TVoidTask server(TPoller& poller, TAddress address, int buffer_size)
{
typename TPoller::TSocket socket(poller, address.Domain());
socket.Bind(address);
socket.Listen();
std::cerr << "Listening on: " << socket.LocalAddr()->ToString() << std::endl;
while (true) {
auto client = co_await socket.Accept();
client_handler<debug>(std::move(client), buffer_size);
}
co_return;
}
template<typename TPoller>
void run(bool debug, TAddress address, int buffer_size)
{
NNet::TLoop<TPoller> loop;
if (debug) {
server<true>(loop.Poller(), std::move(address), buffer_size);
} else {
server<false>(loop.Poller(), std::move(address), buffer_size);
}
loop.Loop();
}
void usage(const char* name) {
std::cerr << name << " [--port 80] [--method select|poll|epoll|kqueue] [--debug] [--buffer-size 100000] [--help]" << std::endl;
std::exit(1);
}
int main(int argc, char** argv) {
NNet::TInitializer init;
int port = 0;
int buffer_size = 128;
std::string method = "select";
bool debug = false;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--port") && i < argc-1) {
port = atoi(argv[++i]);
} else if (!strcmp(argv[i], "--method") && i < argc-1) {
method = argv[++i];
} else if (!strcmp(argv[i], "--debug")) {
debug = true;
} else if (!strcmp(argv[i], "--buffer-size") && i < argc-1) {
buffer_size = atoi(argv[++i]);
} else if (!strcmp(argv[i], "--help")) {
usage(argv[0]);
}
}
if (port == 0) { port = 8888; }
if (buffer_size == 0) { buffer_size = 128; }
TAddress address{"::", port};
std::cerr << "Method: " << method << "\n";
if (method == "select") {
run<TSelect>(debug, address, buffer_size);
}
else if (method == "poll") {
run<TPoll>(debug, address, buffer_size);
}
#ifdef HAVE_EPOLL
else if (method == "epoll") {
run<TEPoll>(debug, address, buffer_size);
}
#endif
#ifdef HAVE_URING
else if (method == "uring") {
run<TUring>(debug, address, buffer_size);
}
#endif
#ifdef HAVE_KQUEUE
else if (method == "kqueue") {
run<TKqueue>(debug, address, buffer_size);
}
#endif
#ifdef HAVE_IOCP
else if (method == "iocp") {
run<TIOCp>(debug, address, buffer_size);
}
#endif
else {
std::cerr << "Unknown method\n";
}
return 0;
}
This page demonstrates both echo client and echo server applications using various pollers. Feel free to explore the full source code on GitHub: Echo Client Source Echo Server Source