180 lines
5.8 KiB
C
180 lines
5.8 KiB
C
//
|
|
// Created by Ajurna on 29/07/2025.
|
|
//
|
|
|
|
#include "server03.h"
|
|
|
|
#include <pthread.h>
|
|
|
|
#include "data.h"
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <stdbool.h>
|
|
|
|
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;
|
|
} |