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.
This commit is contained in:
Barry Kane 2021-09-10 15:03:02 +01:00
parent 33bc9bcda0
commit 7047d0ee08
4 changed files with 87 additions and 24 deletions

View File

@ -3,6 +3,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h>
#include <stdbool.h>
#include <pthread.h> #include <pthread.h>
#include <ncurses.h> #include <ncurses.h>
#include <arpa/inet.h> #include <arpa/inet.h>
@ -13,47 +15,73 @@
#define PORT 5000 #define PORT 5000
#define SA struct sockaddr #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]; char sendBuffer[MAX];
int characterindex; int characterindex;
while (1) while (!shouldExit)
{ {
bzero(sendBuffer, MAX); bzero(sendBuffer, MAX);
wprintw(stdscr, "COMM-LINK> "); wprintw(threadParameters->window, "\n\n\nCOMM-LINK> ");
if(wgetnstr(stdscr, sendBuffer, MAX) == ERR) 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); userInputSanatize(sendBuffer, MAX);
if(sendBuffer[0] == '\n') if(sendBuffer[0] == '\n')
{ {
continue; 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]; char receiveBuffer[MAX];
while (1) while (!shouldExit)
{ {
read((long)sockfd, receiveBuffer, MAX); read(threadParameters->socketDescriptor, receiveBuffer, MAX);
slowPrintNcurses("\nUSER-MESSAGE: ", 8000); slowPrintNcurses("USER-MESSAGE: ", 8000, threadParameters->window);
slowPrintNcurses(receiveBuffer, 8000); slowPrintNcurses(receiveBuffer, 8000, threadParameters->window);
slowPrintNcurses("\nCOMM-LINK (CONT.)> ", 8000);
bzero(receiveBuffer, MAX); bzero(receiveBuffer, MAX);
} }
pthread_exit(NULL);
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int sockfd, connfd; int sockfd, connfd;
struct sockaddr_in servaddr, cli; 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: // Give me a socket, and make sure it's working:
sockfd = socket(AF_INET, SOCK_STREAM, 0); sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) if (sockfd == -1)
@ -93,15 +121,48 @@ int main(int argc, char **argv)
// Setup Ncurses: // Setup Ncurses:
initscr(); initscr();
scrollok(stdscr, true);
// Run a thread to send messages, and use main to recieve. // Create two pointers to structs to pass arguments to the threads:
pthread_create(&messagingThread, NULL, messageSender, (void *)(long)sockfd); threadparameters * logArea;
messageReceiver((void *)(long)sockfd); threadparameters * messageArea;
// Close the socket. 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);
// 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); close(sockfd);
// Free the structs:
free(logArea);
free(messageArea);
// Unsetup Ncurses: // Unsetup Ncurses:
endwin(); endwin();
// Say Goodbye:
slowPrint("\nThank you for choosing Silverkin Industries, valued customer!\n", 8000);
} }

View File

@ -172,7 +172,7 @@ int main()
fflush(stdout); fflush(stdout);
for (int sendIndex = 0; sendIndex < clientsAmount; sendIndex++) 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)); write(clientSockets[sendIndex], receiveBuffer, sizeof(receiveBuffer));
} }

View File

@ -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; int characterIndex = 0;
while(stringToPrint[characterIndex] != '\0') while(stringToPrint[characterIndex] != '\0')
{ {
addch(stringToPrint[characterIndex]); waddch(window, stringToPrint[characterIndex]);
// Refresh the ncurses screen. // Refresh the ncurses screen.
refresh(); wrefresh(window);
usleep(delay); usleep(delay);
characterIndex++; characterIndex++;
} }
wrefresh(window);
} }

View File

@ -3,12 +3,13 @@
#ifndef TEXTEFFECTS_H_ #ifndef TEXTEFFECTS_H_
#define TEXTEFFECTS_H_ #define TEXTEFFECTS_H_
#include <stdio.h> #include <stdio.h>
#include <ncurses.h>
// A fancy, character by character print. Similar to a serial terminal with lower baud rate. // A fancy, character by character print. Similar to a serial terminal with lower baud rate.
void slowPrint(char * stringToPrint, int delay); void slowPrint(char * stringToPrint, int delay);
// The same, altered to work with Ncurses. // 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. // 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"; char * logostring = " ///////\n //////////////////////////////////////////\n ///////////////////////////////////////////////////////////\n ////////// ////////////////////////////\n ### # # # # ##### ### # # # # # /////////////////\n ### # # # # ## # # ## # ## # //////////////\n ## # # # # # ### # # # # # # /////////\n #### # ### # ##### # # # # # # ## ///////\n # ## # ##### # # ### ### ### # ##### ### ////// \n # # # # # # # # ## # # # # ## ## ////\n # # # # # # # # ## # ### # # ## //\n # # ### ##### ##### ### # # # # #### ### //\n";