From e2ef744e87c5652f144ee47ba878038f5097cbea Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Sat, 19 Aug 2023 00:18:03 +0100 Subject: [PATCH] Moved scheme initialization to main thread, added basic networking The server can now listen on a port and send data to a client. --- src/server/main.c | 100 ++++++++++++++++++++++++++++++-- src/server/scheme-integration.c | 14 +---- src/server/scheme-integration.h | 14 ++--- 3 files changed, 106 insertions(+), 22 deletions(-) diff --git a/src/server/main.c b/src/server/main.c index 123c50d..1b676c5 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -4,22 +4,114 @@ // | See end of file for copyright notice. | // ========================================= #include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include "scheme-integration.h" +static const int PORT = 5000; +static const int CONCURRENT_PLAYER_COUNT = 256; + int main (int argc, char ** argv) { // Print a welcome message: printf("SilverMUD Server - Starting Now.\n" "================================\n"); - // Create the Scheme thread: - pthread_t schemeThread; - pthread_create(&schemeThread, NULL, schemeThreadHandler, NULL); + // Initialize Scheme: + scm_init_guile(); + // Start the REPL server on a UNIX socket: + scm_c_eval_string("(begin (use-modules (system repl server))" + "(if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\"))" + "(spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); + + // Create a socket to listen for connections on: + int masterSocket = socket(AF_INET, SOCK_STREAM, 0); + if (masterSocket < 0) + { + fprintf(stderr, "Failed to create socket. Aborting.\n"); + exit(EXIT_FAILURE); + } + + // Allow reusing the address so that quick re-launching doesn't fail: + //setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); + //setsockopt(masterSocket, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)); + + // Setup the server address struct to bind the master socket to: + struct sockaddr_in serverAddress; + memset(&serverAddress, 0, sizeof(struct sockaddr_in)); + + // Assign the IP address and port to the server address struct: + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(PORT); + + // Bind the master socket to the server address: + if ((bind(masterSocket, (struct sockaddr *)&serverAddress, sizeof(struct sockaddr_in))) != 0) + { + fprintf(stderr, "Failed to bind socket. Aborting.\n"); + exit(EXIT_FAILURE); + } + + // Begin listening: + if ((listen(masterSocket, CONCURRENT_PLAYER_COUNT)) != 0) + { + fprintf(stderr, "Failed to begin listening on the master socket. Aborting.\n"); + exit(EXIT_FAILURE); + } + + // Create an epoll instance for managing connections, and add the master socket to it: + int connectedClients = epoll_create(CONCURRENT_PLAYER_COUNT); + if (connectedClients < 0) + { + fprintf(stderr, "Failed to create epoll instance. Aborting.\n"); + exit(EXIT_FAILURE); + } + struct epoll_event watchedEvents; + watchedEvents.events = EPOLLIN; + watchedEvents.data.fd = masterSocket; + epoll_ctl(connectedClients, EPOLL_CTL_ADD, masterSocket, &watchedEvents); + + int eventsCount = 0; + struct epoll_event events[1024]; + + // Start a REPL thread: + pthread_t schemeREPLThread; + pthread_create(&schemeREPLThread, NULL, schemeREPLHandler, NULL); + + while (true) + { + eventsCount = epoll_wait(connectedClients, events, 1024, -1); + if (eventsCount == -1) + { + fprintf(stderr, "epoll_wait() failed. Aborting.\n"); + exit(EXIT_FAILURE); + } + for (int index = 0; index < eventsCount; index++) + { + // If it's the master socket, it's a new client connecting: + if (events[index].data.fd == masterSocket) + { + int newSocket = accept(masterSocket, NULL, NULL); + send(newSocket, "Hello, world!", 13, 0); + close(newSocket); + } + } + } + // Wait for all other threads to terminate: - pthread_join(schemeThread, NULL); + pthread_join(schemeREPLThread, NULL); // Return a successful status code to the operating system: return 0; diff --git a/src/server/scheme-integration.c b/src/server/scheme-integration.c index 2da7cb0..b1b98f2 100644 --- a/src/server/scheme-integration.c +++ b/src/server/scheme-integration.c @@ -7,23 +7,15 @@ #include "scheme-integration.h" -// The function ran by the Scheme thread which initializes the REPL: -void * schemeThreadHandler (void * threadParameters) +// The function ran by the Scheme thread which runs a text-based REPL: +void * schemeREPLHandler (void * threadParameters) { - // Unpack the parameters given to the thread: - struct SchemeThreadArguments * arguments = threadParameters; - - // Initialize GNU Guile: + // Initialize Scheme: scm_init_guile(); // Enable Readline support: scm_c_eval_string("(begin (use-modules (ice-9 readline)) (activate-readline))"); - // Start the REPL server on a UNIX socket: - scm_c_eval_string("(begin (use-modules (system repl server))" - "(if (file-exists? \"silvermud-repl\") (delete-file \"silvermud-repl\"))" - "(spawn-server (make-unix-domain-server-socket #:path \"silvermud-repl\")))"); - // Start a Scheme REPL: scm_shell(0, NULL); diff --git a/src/server/scheme-integration.h b/src/server/scheme-integration.h index 135a650..860427b 100644 --- a/src/server/scheme-integration.h +++ b/src/server/scheme-integration.h @@ -6,14 +6,14 @@ #ifndef SCHEME_INTEGRATION_H #define SCHEME_INTEGRATION_H -struct SchemeThreadArguments -{ - -}; - +struct SchemeThreadArguments +{ + +}; + // The function ran by the Scheme thread which initializes the REPL: -void * schemeThreadHandler (void * threadParameters); - +void * schemeREPLHandler (void * threadParameters); + #endif // ========================================================== // | End of scheme-integration.h, copyright notice follows. |