From 7047d0ee08dd522709d3130fa340d33f4ab5e23f Mon Sep 17 00:00:00 2001 From: Barry Kane Date: Fri, 10 Sep 2021 15:03:02 +0100 Subject: [PATCH] Added two-window messaging to the client. Client now has two seperate Ncurses windows for sending and receiving. Added SIGINT handler which sets a global boolean to gracefully exit and free memory. Sending and Receiving are now on their own threads. A pointer-to-struct is now passed to the threads. The main thread will now wait to cancel the threads upon receiving SIGINT. slowPrintNcurses now takes a window argument. The server now doesn't check that a client receives the message that they sent, allowing for full chat history. --- src/SilverMUDClient.c | 99 ++++++++++++++++++++++++++++++++++-------- src/SilverMUDServer.c | 2 +- src/misc/texteffects.c | 7 +-- src/misc/texteffects.h | 3 +- 4 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/SilverMUDClient.c b/src/SilverMUDClient.c index 6dc243e..18a4a6a 100644 --- a/src/SilverMUDClient.c +++ b/src/SilverMUDClient.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -13,47 +15,73 @@ #define PORT 5000 #define SA struct sockaddr -void * messageSender(void * sockfd) +// A struct for passing arguments to our threads containing a file descriptor and a window pointer: +typedef struct threadparameters { + int socketDescriptor; + WINDOW * window; +} threadparameters; + +// A globally availible exit boolean. +bool shouldExit = false; + +void sigintHandler(int signal) +{ + shouldExit = true; +} + +void * messageSender(void * parameters) +{ + // Takes user input in a window, sanatizes it, and sends it to the server: + struct threadparameters *threadParameters = parameters; char sendBuffer[MAX]; int characterindex; - - while (1) + + while (!shouldExit) { bzero(sendBuffer, MAX); - wprintw(stdscr, "COMM-LINK> "); - if(wgetnstr(stdscr, sendBuffer, MAX) == ERR) + wprintw(threadParameters->window, "\n\n\nCOMM-LINK> "); + if(wgetnstr(threadParameters->window, sendBuffer, MAX) == ERR) { - exit(0); + // Quit if there's any funny business with getting input: + pthread_exit(NULL); } userInputSanatize(sendBuffer, MAX); if(sendBuffer[0] == '\n') { continue; } - write((long)sockfd, sendBuffer, MAX); + write(threadParameters->socketDescriptor, sendBuffer, MAX); } + pthread_exit(NULL); } -void * messageReceiver(void * sockfd) +void * messageReceiver(void * parameters) { + // Takes messages from the server and prints them to the chat log window: + struct threadparameters *threadParameters = parameters; char receiveBuffer[MAX]; - while (1) + while (!shouldExit) { - read((long)sockfd, receiveBuffer, MAX); - slowPrintNcurses("\nUSER-MESSAGE: ", 8000); - slowPrintNcurses(receiveBuffer, 8000); - slowPrintNcurses("\nCOMM-LINK (CONT.)> ", 8000); + read(threadParameters->socketDescriptor, receiveBuffer, MAX); + slowPrintNcurses("USER-MESSAGE: ", 8000, threadParameters->window); + slowPrintNcurses(receiveBuffer, 8000, threadParameters->window); bzero(receiveBuffer, MAX); } + pthread_exit(NULL); } int main(int argc, char **argv) { int sockfd, connfd; struct sockaddr_in servaddr, cli; - pthread_t messagingThread; + pthread_t sendingThread; + pthread_t receivingThread; + + // Set the SIGINT handler. + signal(SIGINT, sigintHandler); + // Give me a socket, and make sure it's working: sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) @@ -93,15 +121,48 @@ int main(int argc, char **argv) // Setup Ncurses: initscr(); - scrollok(stdscr, true); + + // Create two pointers to structs to pass arguments to the threads: + threadparameters * logArea; + threadparameters * messageArea; - // Run a thread to send messages, and use main to recieve. - pthread_create(&messagingThread, NULL, messageSender, (void *)(long)sockfd); - messageReceiver((void *)(long)sockfd); + logArea = malloc(sizeof(*logArea)); + messageArea = malloc(sizeof(*messageArea)); + + // Make the windows for the structs, and pass the socket descriptor: + logArea->window = newwin(LINES - 5, COLS - 2, 1, 1); + logArea->socketDescriptor = sockfd; + messageArea->window = newwin(3, COLS, LINES - 3, 0); + messageArea->socketDescriptor = sockfd; + + // Set the two windows to scroll: + scrollok(logArea->window, true); + scrollok(messageArea->window, true); - // Close the socket. + // Run a thread to send messages, and use main to recieve: + pthread_create(&sendingThread, NULL, messageSender, messageArea); + pthread_create(&receivingThread, NULL, messageReceiver, logArea); + + // Wait for SIGINT to change + while(!shouldExit) + { + sleep(250); + } + + // Close the threads: + pthread_cancel(sendingThread); + pthread_cancel(receivingThread); + + // Close the socket: close(sockfd); + // Free the structs: + free(logArea); + free(messageArea); + // Unsetup Ncurses: endwin(); + + // Say Goodbye: + slowPrint("\nThank you for choosing Silverkin Industries, valued customer!\n", 8000); } diff --git a/src/SilverMUDServer.c b/src/SilverMUDServer.c index 46d213c..32b7255 100644 --- a/src/SilverMUDServer.c +++ b/src/SilverMUDServer.c @@ -172,7 +172,7 @@ int main() fflush(stdout); for (int sendIndex = 0; sendIndex < clientsAmount; sendIndex++) { - if(sendIndex != i && clientSockets[sendIndex] != STDIN_FILENO && clientSockets[sendIndex] != STDOUT_FILENO && clientSockets[sendIndex] != STDERR_FILENO) + if(clientSockets[sendIndex] != STDIN_FILENO && clientSockets[sendIndex] != STDOUT_FILENO && clientSockets[sendIndex] != STDERR_FILENO) { write(clientSockets[sendIndex], receiveBuffer, sizeof(receiveBuffer)); } diff --git a/src/misc/texteffects.c b/src/misc/texteffects.c index 0078dc3..875b3cc 100644 --- a/src/misc/texteffects.c +++ b/src/misc/texteffects.c @@ -17,15 +17,16 @@ void slowPrint(char * stringToPrint, int delay) } } -void slowPrintNcurses(char * stringToPrint, int delay) +void slowPrintNcurses(char * stringToPrint, int delay, WINDOW * window) { int characterIndex = 0; while(stringToPrint[characterIndex] != '\0') { - addch(stringToPrint[characterIndex]); + waddch(window, stringToPrint[characterIndex]); // Refresh the ncurses screen. - refresh(); + wrefresh(window); usleep(delay); characterIndex++; } + wrefresh(window); } diff --git a/src/misc/texteffects.h b/src/misc/texteffects.h index 6cb6729..d75e92c 100644 --- a/src/misc/texteffects.h +++ b/src/misc/texteffects.h @@ -3,12 +3,13 @@ #ifndef TEXTEFFECTS_H_ #define TEXTEFFECTS_H_ #include +#include // A fancy, character by character print. Similar to a serial terminal with lower baud rate. void slowPrint(char * stringToPrint, int delay); // The same, altered to work with Ncurses. -void slowPrintNcurses(char * stringToPrint, int delay); +void slowPrintNcurses(char * stringToPrint, int delay, WINDOW * window); // A string containing an ASCII art version of the Silverkin Industries logo. char * logostring = " ///////\n //////////////////////////////////////////\n ///////////////////////////////////////////////////////////\n ////////// ////////////////////////////\n ### # # # # ##### ### # # # # # /////////////////\n ### # # # # ## # # ## # ## # //////////////\n ## # # # # # ### # # # # # # /////////\n #### # ### # ##### # # # # # # ## ///////\n # ## # ##### # # ### ### ### # ##### ### ////// \n # # # # # # # # ## # # # # ## ## ////\n # # # # # # # # ## # ### # # ## //\n # # ### ##### ##### ### # # # # #### ### //\n";