/** * Copyright (c) 2025 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "server.h" #include "assert/assert.h" #include "console/console.h" #include void serverLocalStart(const serverlocalstart_t start) { //Create the socket SERVER.local.sockDesc = socket(AF_INET, SOCK_STREAM, 0); if(SERVER.local.sockDesc < 0) { assertUnreachable("Failed to create socket"); } //Bind the socket SERVER.local.sockAddr.sin_family = AF_INET; SERVER.local.sockAddr.sin_addr.s_addr = INADDR_ANY; SERVER.local.sockAddr.sin_port = htons(start.port); if(bind( SERVER.local.sockDesc, (struct sockaddr*)&SERVER.local.sockAddr, sizeof(SERVER.local.sockAddr) ) < 0) { assertUnreachable("Failed to bind socket"); } //Listen on the socket if(listen(SERVER.local.sockDesc, 5) < 0) { assertUnreachable("Failed to listen on socket"); } // Begin the server thread. consolePrint("Server started."); // Start the server thread SERVER.local.threadState = SERVER_LOCAL_THREAD_STATE_STARTING; pthread_create(&SERVER.local.thread, NULL, serverLocalThread, NULL); while(SERVER.local.threadState == SERVER_LOCAL_THREAD_STATE_STARTING) { usleep(SERVER_LOCAL_THREAD_SLEEP_TIME); } } void serverLocalStop() { SERVER.local.threadState = SERVER_LOCAL_THREAD_STATE_STOPPING; while(SERVER.local.threadState == SERVER_LOCAL_THREAD_STATE_STOPPING) { usleep(SERVER_LOCAL_THREAD_SLEEP_TIME); } // Stop the server thread. pthread_cancel(SERVER.local.thread); //Close the socket close(SERVER.local.sockDesc); // End the server thread. consolePrint("Server stopped."); } void * serverLocalThread(void *arg) { SERVER.local.threadState = SERVER_LOCAL_THREAD_STATE_RUNNING; fd_set readfds; int32_t activity; int32_t error; struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = SERVER_LOCAL_THREAD_ACCEPT_SLEEP_TIME; // Set the socket to non-blocking mode int flags = fcntl(SERVER.local.sockDesc, F_GETFL, 0); fcntl(SERVER.local.sockDesc, F_SETFL, flags | O_NONBLOCK); while(SERVER.local.threadState == SERVER_LOCAL_THREAD_STATE_RUNNING) { FD_ZERO(&readfds); FD_SET(SERVER.local.sockDesc, &readfds); activity = select( SERVER.local.sockDesc + 1, &readfds, NULL, NULL, &timeout ); if(activity < 0) assertUnreachable("Select error"); if(!FD_ISSET(SERVER.local.sockDesc, &readfds)) { continue; } // Accept incoming connections int32_t clientSockDesc = accept(SERVER.local.sockDesc, NULL, NULL); if(clientSockDesc < 0) { printf("Failed to accept connection\n"); continue; } // Now, is the server full? if(SERVER.clientCount >= SERVER_CLIENT_COUNT_MAX) { consolePrint("Server is full, rejecting connection."); // TODO: Write a reason to client. close(clientSockDesc); continue; } // Create client. client_t *client = &SERVER.clients[SERVER.clientCount]; clientInit(client, (clientinit_t){ .type = CLIENT_TYPE_REMOTE, .remote = { .clientSockDesc = clientSockDesc } }); SERVER.clientCount++; } SERVER.local.threadState = SERVER_LOCAL_THREAD_STATE_STOPPED; return NULL; }