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:
parent
33bc9bcda0
commit
7047d0ee08
|
@ -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);
|
|
||||||
|
// 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.
|
logArea = malloc(sizeof(*logArea));
|
||||||
pthread_create(&messagingThread, NULL, messageSender, (void *)(long)sockfd);
|
messageArea = malloc(sizeof(*messageArea));
|
||||||
messageReceiver((void *)(long)sockfd);
|
|
||||||
|
// 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);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
|
|
Loading…
Reference in New Issue