diff --git a/CMakeLists.txt b/CMakeLists.txt index 155777a35e5fec609c889d6e42802a806631a75c..6ea93eec2d9886081539f77b895db6823d37c509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_C_STANDARD 99) set(CMAKE_C_FLAGS "-lncurses -lpthread") add_executable(client chat.c chat-client.c) -add_executable(server chat.c chat-server.c chat-server-processes.c) +add_executable(server chat.c chat-server.c chat-server-threads.c) add_executable(server-iterative chat.c chat-server.c chat-server-iterative.c) -add_executable(server-processes chat.c chat-server.c chat-server-processes.c) \ No newline at end of file +add_executable(server-processes chat.c chat-server.c chat-server-processes.c) +add_executable(server-threads chat.c chat-server.c chat-server-threads.c) \ No newline at end of file diff --git a/chat-client.c b/chat-client.c index 876ad300c1c3d8038bde96c410ee2e909aa974b5..55868ce37027fb199918749d35740c6f142d7e86 100644 --- a/chat-client.c +++ b/chat-client.c @@ -15,16 +15,13 @@ void initialize_curses(struct io_windows *windows); void initialize_threads(struct io_threads *threads, struct io_windows *windows); void *handle_inputs(void *window_arg); void *handle_outputs(void *window_arg); -#pragma clang diagnostic push -#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" -void on_signal(); -#pragma clang diagnostic pop +void on_signal(int sig); void cleanup(); -bool running = true; int socket_fd; pthread_mutex_t mutex; int initial_cursor_state; +bool running = true; int main() { struct io_windows windows; diff --git a/chat-server-iterative.c b/chat-server-iterative.c index df4de5751dcbc010bae1a0fb2a25e44e187fb033..a47814ae7b2d5a2eb7fedd5f6eed31b59dfceeb8 100644 --- a/chat-server-iterative.c +++ b/chat-server-iterative.c @@ -5,10 +5,10 @@ int accept_connection(int listening_socket_fd); void manage_chat(int *socket_fd, char **client_names, int number_of_clients); void on_signal(int sig); -bool running = true; int client_count; int *client_socket_fd; bool *disconnected; +bool running = true; int main(int argc, const char **argv) { if (argc < 2) { diff --git a/chat-server-processes.c b/chat-server-processes.c index 4fdb89d80846d38d620fccdb07c718f3c76a03bc..e0a00f3a12cd1817fc59b72de86c836129962b97 100644 --- a/chat-server-processes.c +++ b/chat-server-processes.c @@ -9,7 +9,6 @@ void on_signal_listener(int sig); void on_signal_handler(int sig); void on_sigusr1(int sig); -bool running = true; int pipe_to_repeater[2]; // write messages from clients to [1] -- repeater will read from [0] int listening_socket_fd; int accepted_socket_fd; @@ -17,6 +16,7 @@ int *client_socket_fd_list; pid_t *client_handler_pid_list; int client_list_length; char name[BUFFER_SIZE]; +bool running = true; int main(int argc, const char **argv) { if (argc != 2) { diff --git a/chat-server-threads.c b/chat-server-threads.c new file mode 100644 index 0000000000000000000000000000000000000000..d2769bc615e5d1fa246d06d598c71d34d410daf8 --- /dev/null +++ b/chat-server-threads.c @@ -0,0 +1,125 @@ +#include <pthread.h> +#include "chat-server.h" +#include "chat.h" + +void manage_server(int connect_socket_fd); +void *handle_client(void *args); +void on_signal(int sig); +void repeat_message(int *client_fd_list, int list_length, char *message); + +int listening_socket_fd; +int *client_socket_fd_list; +int client_list_length; +pthread_mutex_t mutex; +bool running = true; + +int main(int argc, const char **argv) { + if (argc != 2) { + fprintf(stderr, "Usage: ./server <portnumber>\n"); + exit(1); + } + unsigned short port = (unsigned short)strtol(argv[1], NULL, 10); + struct sockaddr_in server_socket_address; + listening_socket_fd = listen_to_port(port, &server_socket_address); + sigset(SIGINT, on_signal); + sigset(SIGABRT, on_signal); + sigset(SIGKILL, on_signal); + sigset(SIGSEGV, on_signal); + sigset(SIGTERM, on_signal); + display_host_info(port); + pthread_mutex_init(&mutex, NULL); + manage_server(listening_socket_fd); +} + +void manage_server(int connect_socket_fd) { + struct sockaddr_in client_address; + unsigned int client_address_length = sizeof(client_address); + client_socket_fd_list = calloc(CLIENT_QUEUE_DEPTH, sizeof(int)); // initial size + client_list_length = CLIENT_QUEUE_DEPTH; + char buffer[BUFFER_SIZE]; + bzero(buffer, BUFFER_SIZE); + pthread_t tid; + while (running) { + bzero((char *)&client_address, sizeof(struct sockaddr_in)); + int *accepted_socket_fd = malloc(sizeof(int)); // so it's not on the original thread's stack + *accepted_socket_fd = accept(connect_socket_fd, (struct sockaddr *)&client_address, &client_address_length); + pthread_create(&tid, NULL, handle_client, accepted_socket_fd); + } +} + +void *handle_client(void *args) { + int socket_fd = *((int *)args); // so it's on this thread's stack + free(args); + pthread_detach(pthread_self()); // so this thread can be reaped when it terminates + pthread_mutex_lock(&mutex); + add_client(socket_fd, &client_socket_fd_list, 0, NULL, &client_list_length); + pthread_mutex_unlock(&mutex); + char name[BUFFER_SIZE]; + char *buffer = malloc(BUFFER_SIZE); + buffer = receive_message(socket_fd, buffer); + printf("[STATUS]\tAccepted connection from %s.\n", buffer); + strcpy(buffer, "[SERVER]\tPlease type your name."); + send_message(socket_fd, buffer); + buffer = receive_message(socket_fd, buffer); + printf("[STATUS]\tClient identified as %s.\n", buffer); + strncpy(name, buffer, BUFFER_SIZE); + sprintf(buffer, "[SERVER]\tWelcome, %s.", name); + send_message(socket_fd, buffer); + printf("[STATUS]\tThread %ld is listening for messages from %s.\n", (long)pthread_self(), name); + char *message_to_repeat = malloc(BUFFER_SIZE + strlen(name) + 3); + sprintf(message_to_repeat, "[SERVER]\t%s has joined the chat.", name); + bool me_running = true; + while (running && me_running) { + pthread_mutex_lock(&mutex); + repeat_message(client_socket_fd_list, client_list_length, message_to_repeat); + pthread_mutex_unlock(&mutex); + buffer = receive_message(socket_fd, buffer); + sprintf(message_to_repeat, "[%s]\t%s", name, buffer); + if (!strncmp(buffer, "EXIT", 4)) { + printf("%s\n", message_to_repeat); + pthread_mutex_lock(&mutex); + send_message(socket_fd, message_to_repeat); + pthread_mutex_unlock(&mutex); + sprintf(message_to_repeat, "[SERVER]\t%s has left the chat.", name); + me_running = false; + } + } + pthread_mutex_lock(&mutex); + remove_client(socket_fd, client_socket_fd_list, NULL); + repeat_message(client_socket_fd_list, client_list_length, message_to_repeat); + pthread_mutex_unlock(&mutex); + close(socket_fd); + free(message_to_repeat); + free(buffer); + return NULL; +} + +void repeat_message(int *client_fd_list, int list_length, char *message) { + printf("%s\n", message); + for (int i = 0; i < list_length; i++) { + if (client_fd_list[i]) { + send_message(client_fd_list[i], message); + } + } + bzero(message, BUFFER_SIZE); +} + +void on_signal(int sig) { + sigset(SIGINT, SIG_IGN); + sigset(SIGABRT, SIG_IGN); + sigset(SIGKILL, SIG_IGN); + sigset(SIGSEGV, SIG_IGN); + sigset(SIGTERM, SIG_IGN); + running = false; + close(listening_socket_fd); + pthread_mutex_lock(&mutex); + for(int i=0; i<client_list_length; i++) { + if(client_socket_fd_list[i]) { + send_message(client_socket_fd_list[i], "[SERVER]\tServer is terminating. EXIT"); + close(client_socket_fd_list[i]); + } + } + pthread_mutex_unlock(&mutex); + pthread_mutex_destroy(&mutex); + exit(128 + sig); +} \ No newline at end of file diff --git a/chat-server.c b/chat-server.c index f4a54d8eb66dbaa9d98958ac8af0e5f3b6b08f20..290e6f5b99be2a738b250c9e4aaed5b65df0aa49 100644 --- a/chat-server.c +++ b/chat-server.c @@ -1,6 +1,3 @@ -#include "chat.h" -#include <signal.h> -#include <netdb.h> #include "chat-server.h" void display_host_info(unsigned short port) { @@ -56,35 +53,48 @@ int listen_to_port(unsigned short port, struct sockaddr_in *server_socket_addres void add_client(int new_client_fd, int **client_fd_list, pid_t new_handler_pid, pid_t **handler_pid_list, int *list_length) { int i = 0; int *client_list = *client_fd_list; - int *handler_list = *handler_pid_list; + int *handler_list = NULL; + if (handler_pid_list) { + handler_list = *handler_pid_list; + } while (client_list[i] && (i < *list_length)) { i++; } if (i == *list_length) { // need to grow the lists client_list = calloc(2 * (*list_length), sizeof(int)); - handler_list = calloc(2 * (*list_length), sizeof(pid_t)); + if (handler_list) { + handler_list = calloc(2 * (*list_length), sizeof(pid_t)); + } for (int j = 0; j < *list_length; j++) { client_list[j] = (*client_fd_list)[j]; - handler_list[j] = (*handler_pid_list)[j]; + if (handler_list) { + handler_list[j] = (*handler_pid_list)[j]; + } } free(*client_fd_list); free(*handler_pid_list); *list_length *= 2; } client_list[i] = new_client_fd; - handler_list[i] = new_handler_pid; *client_fd_list = client_list; - *handler_pid_list = handler_list; + if (handler_list) { + handler_list[i] = new_handler_pid; + *handler_pid_list = handler_list; + } } void remove_client(int exiting_client_fd, int *client_fd_list, pid_t *handler_pid_list) { if (exiting_client_fd) { while (*client_fd_list != exiting_client_fd) { client_fd_list++; - handler_pid_list++; + if (handler_pid_list) { + handler_pid_list++; + } } *client_fd_list = 0; - *handler_pid_list = 0; + if (handler_pid_list) { + *handler_pid_list = 0; + } } } \ No newline at end of file diff --git a/chat.c b/chat.c index 0ec6683af817f8db09859c5d342697d716e4c4fd..f67408cf970be1e059e614e0831a34ca47e73e6c 100644 --- a/chat.c +++ b/chat.c @@ -1,9 +1,12 @@ +#include <errno.h> #include "chat.h" char *receive_message(int socket_fd, char *message_buffer) { long number_of_received_bytes = read(socket_fd, message_buffer, BUFFER_SIZE - 1); if (number_of_received_bytes < 0) { - perror("Error while receiving message"); + if (!(errno == EBADF && !running)) { + perror("Error while receiving message"); + } number_of_received_bytes = 0; } message_buffer[number_of_received_bytes] = '\0'; diff --git a/chat.h b/chat.h index fc69cf14efff9338b1463f54ec2b373430fa038f..36a57d6edc9f6acb010fb897ca462b5023967aed 100644 --- a/chat.h +++ b/chat.h @@ -12,6 +12,8 @@ #define BUFFER_SIZE 256 +extern bool running; + char *receive_message(int socket_fd, char *message_buffer); void send_message(int socket_fd, char *message);