From a98d66cd6c476dff546b8df5b567f23d11489ad2 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Mon, 25 Jan 2021 16:53:46 +0000 Subject: [PATCH] server: Add basic implementation that accepts connections This creates an asynchronous loop which listens for new connections opening and which will close connections after the client has closed them. This will also read any data that is being received on the sockets and discard it. Signed-off-by: Michael Tremer --- src/main.h | 1 + src/server.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 181 insertions(+), 1 deletion(-) diff --git a/src/main.h b/src/main.h index 06ec914..fb67aeb 100644 --- a/src/main.h +++ b/src/main.h @@ -30,6 +30,7 @@ #define MAX_PARALLEL (1 << 20) struct fireperf_config { + int terminated; int loglevel; enum { FIREPERF_MODE_NONE = 0, diff --git a/src/server.c b/src/server.c index 3fd9471..a936a05 100644 --- a/src/server.c +++ b/src/server.c @@ -18,12 +18,191 @@ # # #############################################################################*/ +#include +#include +#include +#include + #include "logging.h" #include "main.h" #include "server.h" +// Set the size of the read buffer to 1 MiB +#define BUFFER_SIZE 1048576 + +#define SOCKET_BACKLOG 1024 +#define EPOLL_MAX_EVENTS 1024 + +static int create_socket(struct fireperf_config* conf) { + // Open a new socket + int fd = socket(AF_INET6, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (fd < 0) { + ERROR(conf, "Could not open socket: %s\n", strerror(errno)); + goto ERROR; + } + + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_port = htons(conf->port), + }; + + // Bind it to the selected port + int r = bind(fd, &addr, sizeof(addr)); + if (r) { + ERROR(conf, "Could not bind socket: %s\n", strerror(errno)); + goto ERROR; + } + + // Listen + r = listen(fd, SOCKET_BACKLOG); + if (r) { + ERROR(conf, "Could not listen on socket: %s\n", strerror(errno)); + goto ERROR; + } + + DEBUG(conf, "Created listening socket %d\n", fd); + + return fd; + +ERROR: + close(fd); + + return -1; +} + +static int accept_connection(struct fireperf_config* conf, int sockfd) { + struct sockaddr_in6 addr; + socklen_t l = sizeof(addr); + + int fd = -1; + + // The listening socket is ready, there is a new connection waiting to be accepted + do { + fd = accept(sockfd, &addr, &l); + } while (fd < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)); + + if (fd < 0) { + ERROR(conf, "Could not accept a new connection: %s\n", strerror(errno)); + return -1; + } + + DEBUG(conf, "New connection accepted on socket %d\n", fd); + + return fd; +} + +static int handle_io_on_connection(struct fireperf_config* conf, int fd) { + char buffer[BUFFER_SIZE]; + ssize_t bytes_read; + + // Try reading into buffer + do { + bytes_read = recv(fd, buffer, sizeof(buffer), 0); + } while (bytes_read < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)); + + // Error? + if (bytes_read < 0) { + ERROR(conf, "Could not read from socket %d: %s\n", fd, strerror(errno)); + return -1; + + // Connection closed + } else if (bytes_read == 0) { + DEBUG(conf, "Connection %d has closed\n", fd); + return 1; + } + + DEBUG(conf, "Read %zu bytes from socket %d\n", bytes_read, fd); + + return 0; +} + int fireperf_server(struct fireperf_config* conf) { DEBUG(conf, "Launching " PACKAGE_NAME " in server mode\n"); - return 0; + int sockfd = -1; + + int epollfd = -1; + struct epoll_event events[EPOLL_MAX_EVENTS]; + + int r = 1; + + // Create listening socket + sockfd = create_socket(conf); + if (sockfd < 0) + return 1; + + // Initialize epoll() + epollfd = epoll_create1(0); + if (epollfd < 0) { + ERROR(conf, "Could not initialize epoll(): %s\n", strerror(errno)); + return 1; + } + + // Add listening socket + struct epoll_event ev = { + .events = EPOLLIN, + .data.fd = sockfd, + }; + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev)) { + ERROR(conf, "Could not add socket file descriptor to epoll(): %s\n", + strerror(errno)); + goto ERROR; + } + + DEBUG(conf, "Entering main loop...\n"); + + while (!conf->terminated) { + int fds = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, -1); + if (fds < 1) { + ERROR(conf, "epoll_wait() failed: %s\n", strerror(errno)); + goto ERROR; + } + + DEBUG(conf, "%d event(s) are ready\n", fds); + + for (int i = 0; i < fds; i++) { + int fd = events[i].data.fd; + + // The listening socket + if (fd == sockfd) { + int connfd = accept_connection(conf, sockfd); + if (connfd < 0) + goto ERROR; + + // Add the new socket to epoll() + ev.events = EPOLLIN; + ev.data.fd = connfd; + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev)) { + ERROR(conf, "Could not add socket file descriptor to epoll(): %s\n", + strerror(errno)); + goto ERROR; + } + + // One of the connections had IO + } else { + r = handle_io_on_connection(conf, fd); + if (r < 0) + goto ERROR; + + // Connection closed? + else if (r == 1) { + if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL)) { + ERROR(conf, "Could not remove socket file descriptfor from epoll(): %s\n", + strerror(errno)); + } + } + } + } + } + +ERROR: + if (sockfd > 0) + close(sockfd); + + if (epollfd > 0) + close(epollfd); + + return r; } -- 2.47.2