// // Created by Ajurna on 29/07/2025. // #include "server03.h" #include #include "data.h" #include #include #include pthread_mutex_t mutex; pthread_cond_t cond; #define WELCOME "Welcome to budgetchat! What shall I call you?\n" int main() { SOCKET server = get_listen_socket(); SOCKADDR_IN clientAddr; SOCKET client; connections_t *connections = connections_create(64); int clientAddrSize = sizeof(clientAddr); int connection_number = 1; printf("Listening for incoming connections...\n"); while((client = accept(server, (SOCKADDR *)&clientAddr, &clientAddrSize)) != INVALID_SOCKET) { handle_args_t *args = malloc(sizeof(handle_args_t)); args->client = client; args->connection = connection_number++; args->connections = *connections; pthread_t thread; pthread_create(&thread, nullptr, handle_connection, args); } connections_destroy(connections); return 0; } void *handle_connection(void *args) { handle_args_t *handleArgs = args; char buffer[1024] = {0}; int bytesReceived; status_t status = CONNECTED; char *message = malloc(sizeof(char)*1024); char_array_t *data = char_array_create(1024); send(handleArgs->client, WELCOME, sizeof(WELCOME), 0); bytesReceived = recv(handleArgs->client, buffer, sizeof(buffer), 0); char_array_append(data, buffer, bytesReceived); char *username = char_array_get_until_char(data, '\n'); if (username == NULL) { printf("{%d} Failed to parse username\n", handleArgs->connection); connections_remove(&handleArgs->connections, handleArgs->client); free(handleArgs); pthread_exit(NULL); exit(3); } username = trim(username); if (!username_is_valid(username)) { printf("{%d} Invalid username\n", handleArgs->connection); connections_remove(&handleArgs->connections, handleArgs->client); free(handleArgs); pthread_exit(NULL); exit(3); } pthread_mutex_lock(&mutex); sprintf(message, "* The room contains: "); for (int i = 0; i < handleArgs->connections.len; i++) { strcat(message, handleArgs->connections.clients[i].username); strcat(message, " "); } strcat(message, "\n"); send(handleArgs->client, message, strlen(message), 0); pthread_mutex_unlock(&mutex); connections_append(&handleArgs->connections, handleArgs->client, username); sprintf(message, "* %s has entered the room\n", username); broadcast(&handleArgs->connections, &handleArgs->client, message); printf("{%d} Username: %s\n", handleArgs->connection, username); while ((bytesReceived = recv(handleArgs->client, buffer, sizeof(buffer), 0)) > 0) { printf("{%d} Client sent: |%d| \n", handleArgs->connection, bytesReceived); char_array_append(data, buffer, bytesReceived); char *request; while ((request = char_array_get_until_char(data, '\n')) != NULL) { sprintf(message, "[%s] %s\n", username, request); } memset(buffer, 0, sizeof(buffer)); } free(message); free(args); return NULL; } connections_t *connections_create(const int size) { connections_t *connections = malloc(sizeof(connections_t)); connections->size = size; connections->len = 0; connections->clients = calloc(size, sizeof(client_connection_t)); return connections; } void connections_destroy(connections_t *connections) { free(connections->clients); free(connections); } void connections_append(connections_t *connections, SOCKET client, char *username) { if (connections == NULL) { exit(1); } pthread_mutex_lock(&mutex); size_t new_len = connections->len + 1; if (new_len > connections->size) { connections->size = connections->size+64; client_connection_t *new_array = realloc(connections->clients, connections->size * sizeof(client_connection_t)); connections->clients = new_array; if (connections->clients == NULL) { printf("Failed to allocate memory for array\n"); } } connections->clients[connections->size].client = client; connections->clients[connections->size].username = username; connections->len = new_len; printf("{%llu} Connection added\n", connections->size); pthread_mutex_unlock(&mutex); }; void connections_remove(connections_t *connections, SOCKET client) { if (connections == NULL) { exit(1); } pthread_mutex_lock(&mutex); for (int i = 0; i < connections->size; i++) { if (connections->clients[i].client == client) { closesocket(connections->clients[i].client); free(connections->clients[i].username); for (int j = i; j < connections->size; j++) { connections->clients[j] = connections->clients[j+1]; } connections->size--; printf("{%llu} Connection removed\n", connections->size); pthread_mutex_unlock(&mutex); return; } } } void broadcast(const connections_t *connections, const SOCKET *source, const char *message) { pthread_mutex_lock(&mutex); for (size_t i = 0; i < connections->size; i++) { if (connections->clients[i].client != *source) { send(connections->clients[i].client, message, strlen(message), 0); } } pthread_mutex_unlock(&mutex); } char *trim(char *str) { char *end; while (isspace(*str)) str++; if (*str == 0) return str; end = str + strlen(str) - 1; while (end > str && isspace(*end)) end--; end[1] = '\0'; return str; } bool username_is_valid(const char *username) { if (username == NULL) { return false; } for (int i = 0; i < strlen(username); i++) { if (!isalnum(username[i])) { return false; } } return true; }