From 1188ab5090c2e97c65b065b4d8133efa94e25073 Mon Sep 17 00:00:00 2001 From: Christopher Bohn <bohn@unl.edu> Date: Tue, 13 Jul 2021 15:46:47 -0500 Subject: [PATCH] Added thread-based chat server --- CMakeLists.txt | 5 +- chat-client.c | 7 +-- chat-server-iterative.c | 2 +- chat-server-processes.c | 2 +- chat-server-threads.c | 125 ++++++++++++++++++++++++++++++++++++++++ chat-server.c | 30 ++++++---- chat.c | 5 +- chat.h | 2 + 8 files changed, 158 insertions(+), 20 deletions(-) create mode 100644 chat-server-threads.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 155777a..6ea93ee 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 876ad30..55868ce 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 df4de57..a47814a 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 4fdb89d..e0a00f3 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 0000000..d2769bc --- /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 f4a54d8..290e6f5 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 0ec6683..f67408c 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 fc69cf1..36a57d6 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); -- GitLab