From 3d43918dc24a89bccac1b52e92acf1e2b120bcf0 Mon Sep 17 00:00:00 2001 From: Florian Fischer <florian.fl.fischer@fau.de> Date: Thu, 4 Feb 2021 18:48:50 +0100 Subject: [PATCH] Add callback based EchoServer implementation --- apps/EchoServerCallback.cpp | 118 ++++++++++++++++++++++++++++++++++++ apps/meson.build | 6 ++ 2 files changed, 124 insertions(+) create mode 100644 apps/EchoServerCallback.cpp diff --git a/apps/EchoServerCallback.cpp b/apps/EchoServerCallback.cpp new file mode 100644 index 00000000..b6b6a29e --- /dev/null +++ b/apps/EchoServerCallback.cpp @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// Copyright © 2020-2021 Florian Fischer +#include <sys/socket.h> + +#include <array> +#include <cerrno> +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <string> + +#include "Common.hpp" +#include "Debug.hpp" +#include "Runtime.hpp" +#include "io.hpp" +#include "io/Future.hpp" + +using SendFuture = emper::io::SendFuture; +using RecvFuture = emper::io::RecvFuture; + +const std::string HOST = "::"; +const std::string PORT = "12345"; + +const size_t BUF_SIZE = 1024; + +class Client { + public: + int sockfd; + size_t bytes_recv; + size_t bytes_send; + std::array<char, BUF_SIZE> buf; + + Client(int socket) : sockfd(socket), bytes_recv(0), bytes_send(0) {} + + void submitSend() { + SendFuture sf(sockfd, &buf[bytes_send], bytes_recv - bytes_send, MSG_NOSIGNAL); + sf.setCallback([this](int32_t bytes_send) { this->onSend(bytes_send); }); + sf.submit(); + } + + void onSend(int32_t res) { + if (unlikely(res < 0)) { + LOGE("server send failed: " << strerror(errno)); + emper::io::closeAndForget(sockfd); + return; + } + + bytes_send += res; + + // Send again + if (bytes_send < bytes_recv) { + submitSend(); + return; + } + + submitRecv(); + } + + void submitRecv() { + bytes_send = 0; + RecvFuture rf(sockfd, buf.data(), BUF_SIZE, 0); + rf.setCallback([this](int32_t bytes_recv) { this->onRecv(bytes_recv); }); + rf.submit(); + } + + void onRecv(int32_t res) { + if (unlikely(res <= 0)) { + // socket was shutdown + if (res < 0) { + LOGE("server read failed:" << strerror(errno)); + } + + emper::io::closeAndForget(sockfd); + return; + } + + bytes_recv = res; + + if (unlikely(bytes_recv == 5 && strncmp("quit\n", buf.data(), bytes_recv) == 0)) { + exit(EXIT_SUCCESS); + } + + submitSend(); + } +}; + +auto main(int argc, char* argv[]) -> int { + std::string host = HOST; + std::string port = PORT; + + if (argc > 2) { + std::cerr << "Usage: " << argv[0] << " [port]" << std::endl; + exit(EXIT_FAILURE); + } + + if (argc > 1) { + port = std::string(argv[1]); + } + + std::cout << "Echoserver listening on " << host << ":" << port << std::endl; + + Runtime runtime; + auto* listener = emper::io::tcp_listener(host, port, [](int socket) { + auto* client = new Client(socket); + client->submitRecv(); + }); + + if (!listener) { + exit(EXIT_FAILURE); + } + + runtime.scheduleFromAnywhere(*listener); + + runtime.waitUntilFinished(); + + return EXIT_FAILURE; +} diff --git a/apps/meson.build b/apps/meson.build index 015a15df..44ff1edb 100644 --- a/apps/meson.build +++ b/apps/meson.build @@ -16,6 +16,12 @@ echoserver_exe = executable( dependencies: emper_dep, ) +echoserver_callback_exe = executable( + 'echoserver_callback', + 'EchoServerCallback.cpp', + dependencies: emper_dep, +) + echoclient_exe = executable( 'echoclient', 'EchoClient.cpp', -- GitLab