From 7869ef2ace9a487abb0b489ca432b0a8878c5083 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Sat, 2 Nov 2013 03:19:24 +0100 Subject: First step of the connection skeleton Basic connect, socket creating, polling, recving, etc. --- src/network/poller.cpp | 96 ++++++++++++++++++++++++++++++++++++++++++ src/network/poller.hpp | 72 +++++++++++++++++++++++++++++++ src/network/socket_handler.hpp | 62 +++++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 src/network/poller.cpp create mode 100644 src/network/poller.hpp create mode 100644 src/network/socket_handler.hpp (limited to 'src/network') diff --git a/src/network/poller.cpp b/src/network/poller.cpp new file mode 100644 index 0000000..c7d9eb2 --- /dev/null +++ b/src/network/poller.cpp @@ -0,0 +1,96 @@ +#include + +#include +#include +#include + + +Poller::Poller() +{ + std::cout << "Poller()" << std::endl; +#if POLLER == POLL + memset(this->fds, 0, sizeof(this->fds)); + this->nfds = 0; +#endif +} + +Poller::~Poller() +{ + std::cout << "~Poller()" << std::endl; +} + +void Poller::add_socket_handler(std::shared_ptr socket_handler) +{ + // Raise an error if that socket is already in the list + const auto it = this->socket_handlers.find(socket_handler->get_socket()); + if (it != this->socket_handlers.end()) + throw std::runtime_error("Trying to insert SocketHandler already managed"); + + this->socket_handlers.emplace(socket_handler->get_socket(), socket_handler); + socket_handler->set_poller(this); + + // We always watch all sockets for receive events +#if POLLER == POLL + this->fds[this->nfds].fd = socket_handler->get_socket(); + this->fds[this->nfds].events = POLLIN; + this->nfds++; +#endif +} + +void Poller::remove_socket_handler(const socket_t socket) +{ + const auto it = this->socket_handlers.find(socket); + if (it == this->socket_handlers.end()) + throw std::runtime_error("Trying to remove a SocketHandler that is not managed"); + this->socket_handlers.erase(it); + for (size_t i = 0; i < this->nfds; i++) + { + if (this->fds[i].fd == socket) + { + // Move all subsequent pollfd by one on the left, erasing the + // value of the one we remove + for (size_t j = i; j < this->nfds - 1; ++j) + { + this->fds[j].fd = this->fds[j+1].fd; + this->fds[j].events= this->fds[j+1].events; + } + this->nfds--; + } + } +} + +void Poller::poll() +{ +#if POLLER == POLL + std::cout << "Polling:" << std::endl; + for (size_t i = 0; i < this->nfds; ++i) + std::cout << "pollfd[" << i << "]: (" << this->fds[i].fd << ")" << std::endl; + int res = ::poll(this->fds, this->nfds, -1); + if (res < 0) + { + perror("poll"); + throw std::runtime_error("Poll failed"); + } + // We cannot possibly have more ready events than the number of fds we are + // watching + assert(static_cast(res) <= this->nfds); + for (size_t i = 0; i <= this->nfds && res != 0; ++i) + { + if (this->fds[i].revents == 0) + continue; + else if (this->fds[i].revents & POLLIN) + { + auto socket_handler = this->socket_handlers.at(this->fds[i].fd); + socket_handler->on_recv(); + res--; + } + else if (this->fds[i].revents & POLLOUT) + { + auto socket_handler = this->socket_handlers.at(this->fds[i].fd); + socket_handler->on_send(); + res--; + } + } +#endif +} + diff --git a/src/network/poller.hpp b/src/network/poller.hpp new file mode 100644 index 0000000..46a184e --- /dev/null +++ b/src/network/poller.hpp @@ -0,0 +1,72 @@ +#ifndef POLLER_INCLUDED +# define POLLER_INCLUDED + +#include + +#include +#include + +#define POLL 1 +#define EPOLL 2 +#define KQUEUE 3 + +#define POLLER POLL + +#if POLLER == POLL + #include + // TODO, dynamic size, without artificial limit + #define MAX_POLL_FD_NUMBER 4096 +#endif + +/** + * We pass some SocketHandlers to this the Poller, which uses + * poll/epoll/kqueue/select etc to wait for events on these SocketHandlers, + * and call the callbacks when event occurs. + * + * TODO: support for all these pollers: + * - poll(2) (mandatory) + * - epoll(7) + * - kqueue(2) + */ + + +class Poller +{ +public: + explicit Poller(); + ~Poller(); + /** + * Add a SocketHandler to be monitored by this Poller. All receive events + * are always automatically watched. + */ + void add_socket_handler(std::shared_ptr socket_handler); + /** + * Remove (and stop managing) a SocketHandler, designed by the given socket_t. + */ + void remove_socket_handler(const socket_t socket); + /** + * Wait for all watched events, and call the SocketHandlers' callbacks + * when one is ready. + */ + void poll(); + +private: + /** + * A "list" of all the SocketHandlers that we manage, indexed by socket, + * because that's what is returned by select/poll/etc when an event + * occures. + */ + std::unordered_map> socket_handlers; + +#if POLLER == POLL + struct pollfd fds[MAX_POLL_FD_NUMBER]; + nfds_t nfds; +#endif + + Poller(const Poller&) = delete; + Poller(Poller&&) = delete; + Poller& operator=(const Poller&) = delete; + Poller& operator=(Poller&&) = delete; +}; + +#endif // POLLER_INCLUDED diff --git a/src/network/socket_handler.hpp b/src/network/socket_handler.hpp new file mode 100644 index 0000000..165f732 --- /dev/null +++ b/src/network/socket_handler.hpp @@ -0,0 +1,62 @@ +#ifndef SOCKET_HANDLER_INCLUDED +# define SOCKET_HANDLER_INCLUDED + +typedef int socket_t; + +class Poller; + +/** + * An interface, with a series of callbacks that should be implemented in + * subclasses that deal with a socket. These callbacks are called on various events + * (read/write/timeout, etc) when they are notified to a poller + * (select/poll/epoll etc) + */ +class SocketHandler +{ +public: + explicit SocketHandler(): + poller(nullptr) + {} + /** + * Set the pointer to the given Poller, to communicate with it. + */ + void set_poller(Poller* poller) + { + this->poller = poller; + }; + /** + * Happens when the socket is ready to be received from. + */ + virtual void on_recv() = 0; + /** + * Happens when the socket is ready to be written to. + */ + virtual void on_send() = 0; + /** + * Returns the socket that should be handled by the poller. + */ + virtual socket_t get_socket() const = 0; + /** + * Close the connection. + */ + virtual void close() = 0; + +protected: + /** + * A pointer to the poller that manages us, because we need to communicate + * with it, sometimes (for example to tell it that he now needs to watch + * write events for us). Do not ever try to delete it. + * + * And a raw pointer because we are not owning it, it is owning us + * (actually it is sharing our ownership with a Bridge). + */ + Poller* poller; + +private: + SocketHandler(const SocketHandler&) = delete; + SocketHandler(SocketHandler&&) = delete; + SocketHandler& operator=(const SocketHandler&) = delete; + SocketHandler& operator=(SocketHandler&&) = delete; +}; + +#endif // SOCKET_HANDLER_INCLUDED -- cgit v1.2.3