127 lines
3.2 KiB
C
127 lines
3.2 KiB
C
/**
|
|
* 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 <fcntl.h>
|
|
|
|
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;
|
|
} |