#pragma once #include <ev++.h> #include <atomic> #include <functional> #include <memory> #include <net/async_fd.hpp> #include "net/connection_client.hpp" namespace rmrf::net { /** * This class handles raw server sockets and wrapps them with automatic * resource management functions. This class is an internal helper class * utilized by classes like the TCP Server and thus shouldn't be used directly. * @class async_server_socket * @author leondietrich * @date 14/07/21 * @file async_server.hpp * @brief An asynchronous server socket handler. * @see rmrf::net::tcp_server_socket */ class async_server_socket : public std::enable_shared_from_this<async_server_socket> { public: typedef std::shared_ptr<async_server_socket> self_ptr_type; typedef std::function<void(self_ptr_type, std::shared_ptr<connection_client>)> accept_handler_type; typedef std::function<void(self_ptr_type)> error_handler_type; private: auto_fd socket; std::atomic_uint32_t number_of_connected_clients; unsigned int max_number_of_simulataneusly_allowed_clients; accept_handler_type on_accept; accept_handler_type on_overflow; error_handler_type on_error; ::ev::io io; public: /** * This constructor accepts your given file descriptor to the operating systems socket. * @param fd A handle to the socket in form of an auto_fd */ async_server_socket(auto_fd &&fd); /** * This deconstructor automatically unregisteres the socket from libev which in turn automatically removes it from * the watch list of active sockets. However this does not close the socket in the kernels perspective so you must * use always auto_fd. * @brief Automatically unregister the socket from libev */ virtual ~async_server_socket(); /** * This method will return the currently used connection acceptance handler. * Be aware that it will return an invalid state unless you used set_accept_handler * prior to calling this method. * @brief get the current registered accept handler * @return The current accept handler */ inline accept_handler_type get_accept_handler() const { return on_accept; } /** * Use this method in order to set a new incomming socket acceptance handler. * It's best to call this method right after the constructor call of this class. * @brief Set a new acceptance handler * @param value The new handler to set */ inline void set_accept_handler(const accept_handler_type& value) { on_accept = value; } /** * Use this method in order to set a new error handler. It will be called in case of fd errors reported by libev. * @brief Set a new error handler * @param value The error handler to set */ inline void set_error_handler(const error_handler_type& value) { this->on_error = value; } /** * This method sets the overflow handler to use if the maximum number of allowed clients was reached. * The purpose of said handler is to inform the connected client that the connection cant be established * due to isufficient resources. It should transmit the appropriate error message and drop the client. * @brief Set the client overflow handler * @param value The listener that handles clients that couldn't be accepted */ void set_overflow_handler(const accept_handler_type& value) { on_overflow = value; } /** * This method provides you with the current number of connected clients. When a client * disconnects this number will be reduced. When a new client arrives this number will be incremented. * @brief Get the current number of connected clients. * @return The number of connected clients */ [[nodiscard]] inline unsigned int get_number_of_connected_clients() const { return this->number_of_connected_clients; } /** * Use this method in order to set the maximum number of allowed connections. Set it to * 0 in order to disable the limit. If anything other than 0 is set it is highly recommended * to also set an overflow handler. * @brief Set the maximum allowed simultaneus connections. * @param max_connections The maximum number of allowed connections. */ inline void set_maximum_concurrent_connections(unsigned int max_connections) { this->max_number_of_simulataneusly_allowed_clients = max_connections; } protected: virtual std::shared_ptr<connection_client> await_raw_socket_incomming(const auto_fd& socket) = 0; connection_client::destructor_cb_type get_locked_destructor_callback(); private: void cb_ev(::ev::io &w, int events); void client_destructed_cb(exit_status_t exit_status); }; }