diff --git a/backend/src/main.cpp b/backend/src/main.cpp index 4fe2dbd..ddc550c 100644 --- a/backend/src/main.cpp +++ b/backend/src/main.cpp @@ -1,3 +1,5 @@ +#include "tcp.hpp" +#include #include #include #include @@ -7,6 +9,15 @@ int main(void) { - printf("hello\n"); + auto x = mst::TcpListener::bind("0.0.0.0", PORT); + if (!x) { + std::println("{}", x.error()); + return 1; + } + std::println("starting"); + auto listener = x.value(); + { + auto x = listener.loop(); + } return 0; } diff --git a/backend/src/server.cpp b/backend/src/server.cpp new file mode 100644 index 0000000..e69de29 diff --git a/backend/src/server.hpp b/backend/src/server.hpp new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/backend/src/server.hpp @@ -0,0 +1 @@ +#pragma once diff --git a/backend/src/tcp.cpp b/backend/src/tcp.cpp index 596e4b7..74f1123 100644 --- a/backend/src/tcp.cpp +++ b/backend/src/tcp.cpp @@ -1,5 +1,6 @@ #include "tcp.h" #include +#include #include #include #include @@ -15,6 +16,29 @@ #include namespace mst { + +auto event::make_listener_event(int fd) -> Event* +{ + auto ev = (Event*)malloc(sizeof(Event)); + + ev->variant = Listener; + ev->data.listener_fd = fd; + + return ev; +} + +auto event::make_connection_event(TcpConnection connection) -> Event* +{ + auto ev = (Event*)malloc(sizeof(Event)); + auto ptr = (TcpConnection*)std::malloc(sizeof(TcpConnection)); + *ptr = connection; + + ev->variant = Connection; + ev->data.connection = ptr; + + return ev; +} + auto errno_shim(std::string_view message) -> std::string { auto x = strerror(errno); @@ -62,24 +86,61 @@ auto TcpListener::bind(const std::string& host, uint16_t port) return std::unexpected(errno_shim("could not listen")); } - auto epoll_fd = ::epoll_create(0); + auto epoll_fd = ::epoll_create1(0); - auto events_accepted = epoll_event { .events = EPOLLIN, .data = { } }; - if (::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &events_accepted) < 0) { + auto context = event::make_listener_event(socket_fd); + + auto event = epoll_event { .events = EPOLLIN, .data = { .ptr = context } }; + if (::epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event) < 0) { return std::unexpected(errno_shim("could not connect to epoll")); } - return TcpListener(socket_fd, epoll_fd, address); + return TcpListener(epoll_fd, address); } -auto TcpListener::accept() -> Result +auto TcpListener::start() -> Result { - socklen_t size = sizeof(address); - int socket = ::accept(this->listener_fd, (struct sockaddr*)&address, &size); - if (socket < 0) { - return std::unexpected(errno_shim("could not accept")); + epoll_event events[128] = { }; + while (true) { + + auto events_len = ::epoll_wait(this->epoll_fd, events, 128, -1); + if (events_len < 0) { + return std::unexpected(errno_shim("could not poll")); + } + for (int i = 0; i < events_len; ++i) { + auto event = (event::Event*)events[i].data.ptr; + + switch (event->variant) { + case event::Listener: { + socklen_t size = sizeof(address); + int client = ::accept( + events[0].data.fd, (struct sockaddr*)&address, &size); + if (client < 0) { + return std::unexpected(errno_shim("could not accept")); + } + + auto context = event::make_connection_event( + TcpConnection(*this, client)); + + auto poll_event = epoll_event { .events = EPOLLIN, + .data = { .ptr = context } }; + + if (::epoll_ctl( + epoll_fd, EPOLL_CTL_ADD, client, &poll_event) + < 0) { + return std::unexpected( + errno_shim("could not add connection to epoll")); + } + + break; + } + case event::Connection: { + event->data.connection.wake(); + break; + } + } + } } - return TcpConnection(*this, socket); } } diff --git a/backend/src/tcp.h b/backend/src/tcp.h index 7252ab2..30b6538 100644 --- a/backend/src/tcp.h +++ b/backend/src/tcp.h @@ -4,11 +4,29 @@ #include #include #include -#include +#include namespace mst { + template using Result = std::expected; +class TcpConnection; + +namespace event { + enum Variant { Listener, Connection }; + typedef union { + int listener_fd; + TcpConnection* connection; + } Data; + struct Event { + Variant variant; + Data data; + }; + + auto make_listener_event(int fd) -> Event*; + auto make_connection_event(TcpConnection connection) -> Event*; +} + class TcpListener; class TcpConnection { @@ -20,6 +38,7 @@ public: private: int fd; + event::Event event; }; class TcpListener { @@ -27,16 +46,13 @@ class TcpListener { public: static auto bind(const std::string& host, uint16_t port) -> Result; - auto accept() -> Result; + auto start() -> Result; private: - TcpListener(int listener_fd, int epoll_fd, sockaddr_in address) - : listener_fd(listener_fd) - , epoll_fd(epoll_fd) + TcpListener(int epoll_fd, sockaddr_in address) + : epoll_fd(epoll_fd) , address(address) { }; - int listener_fd; int epoll_fd; sockaddr_in address; - std::vector epoll_fds; }; }