Compare commits
10 Commits
33f7d4bcc4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75961f9dab | ||
|
|
2004f084be | ||
|
|
bc996f6ca1 | ||
|
|
e7f7a83c57 | ||
|
|
55cb068585 | ||
|
|
07e742aa4e | ||
|
|
e3cf81ab3f | ||
|
|
2e70901ef8 | ||
| 99dac0f416 | |||
| ad882a84d0 |
13
README.md
13
README.md
@@ -2,11 +2,8 @@
|
||||
|
||||
## TODO
|
||||
|
||||
- Finalize coding style,
|
||||
- **see [STYLE.md](STYLE.md)**
|
||||
- Get everyone's editors set up for clangd/clang-format and git
|
||||
- Make sure everyone's compiler toolchains work
|
||||
- Assign responsibilities to each team member
|
||||
- Improve documentation
|
||||
- Fix cursor going in the wrong place when either user receives a message
|
||||
|
||||
## Build
|
||||
|
||||
@@ -17,11 +14,13 @@ make
|
||||
## Usage
|
||||
|
||||
```
|
||||
./ct # run as server
|
||||
./ct -m server # run as server
|
||||
```
|
||||
|
||||
```
|
||||
./ct client # run as client
|
||||
./ct -m client # run as client
|
||||
```
|
||||
|
||||
Running the program without any options will print a help page with more information.
|
||||
|
||||
Chat logs are stored in `./server.txt` and `./client.txt`.
|
||||
|
||||
37
client.cc
37
client.cc
@@ -35,28 +35,23 @@ closeClient()
|
||||
void *
|
||||
pollForSever(void *args)
|
||||
{
|
||||
int b;
|
||||
char msg[1024], msgf[1033];
|
||||
waitClientArgs *aaa = static_cast<waitClientArgs *>(args);
|
||||
int socketDescriptor = (int)aaa->auxInt;
|
||||
char msg[1024];
|
||||
|
||||
while (1) {
|
||||
server_message_loop:
|
||||
memset(&msg, 0, sizeof(msg)); // clear the buffer
|
||||
bytesRead += recv(socketDescriptor, (char *)&msg, sizeof(msg), 0);
|
||||
|
||||
// not needed for proofs of concept testing
|
||||
/*if(!strcmp(msg, "exit"))
|
||||
{
|
||||
writeToFile(logFileName, msg);
|
||||
displayFile(logFileName, linePos, LOG_LENGTH);
|
||||
break;
|
||||
}*/
|
||||
// cout << "Server: " << msg << endl;
|
||||
// printf("Server: %s\n");
|
||||
memset(&msg, 0, sizeof(msg)); /* clear buffer */
|
||||
b = recv(socketDescriptor, (char *)&msg, sizeof(msg), 0);
|
||||
msg[b] = '\0';
|
||||
bytesRead += b;
|
||||
|
||||
if (msg[0] == '\0')
|
||||
goto server_message_loop;
|
||||
|
||||
writeToFile(logFileName, msg);
|
||||
snprintf(msgf, sizeof(msgf), "Server: %s", msg);
|
||||
writeToFile(logFileName, msgf);
|
||||
|
||||
if (linesInFile(logFileName) > LOG_LENGTH)
|
||||
linePos++;
|
||||
@@ -71,13 +66,14 @@ void
|
||||
setupClient()
|
||||
{
|
||||
// we need 2 things: ip address and port number, in that order
|
||||
// if(argc != 3)
|
||||
//{
|
||||
// cerr << "Usage: ip_address port" << endl; exit(0);
|
||||
// } //grab the IP address and port number
|
||||
|
||||
// create a message buffer
|
||||
char msg[1024];
|
||||
// setup a socket and connection tools
|
||||
|
||||
if (IP_ADDRESS == "0.0.0.0")
|
||||
IP_ADDRESS = "127.0.0.1";
|
||||
|
||||
/* prepare socket and connection tools */
|
||||
struct hostent *host = gethostbyname(IP_ADDRESS.c_str());
|
||||
sockaddr_in sendSockAddr;
|
||||
bzero((char *)&sendSockAddr, sizeof(sendSockAddr));
|
||||
@@ -85,7 +81,8 @@ setupClient()
|
||||
sendSockAddr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)*host->h_addr_list));
|
||||
sendSockAddr.sin_port = htons(PORT_NUM);
|
||||
clientSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
|
||||
// try to connect...
|
||||
|
||||
/* try to connect... */
|
||||
int status =
|
||||
connect(clientSocketDescriptor, (sockaddr *)&sendSockAddr, sizeof(sendSockAddr));
|
||||
|
||||
|
||||
64
ct.1
64
ct.1
@@ -1,22 +1,62 @@
|
||||
.TH "Threaded Network Chat" 1
|
||||
.TH CT 1
|
||||
|
||||
.SH NAME
|
||||
\fBct\fR - Threaded Network Chat
|
||||
ct \- simple threaded network chat program
|
||||
|
||||
.SH SYNOPSIS
|
||||
.SY
|
||||
./ct # start as server
|
||||
|
||||
./ct # start as client
|
||||
|
||||
/quit # while program running
|
||||
.YS
|
||||
.B ct
|
||||
.br
|
||||
.B ct client
|
||||
|
||||
.SH DESCRIPTION
|
||||
.LP
|
||||
\fBct\fR is a simple terminal-based chat application that allows communication
|
||||
between two users over a network connection. The program operates in either
|
||||
server or client mode.
|
||||
|
||||
.LP
|
||||
This program is meant to run as two instances. One for a "host" or "server" user
|
||||
to initiate a session and the other for a "client" user to join the host.
|
||||
When run without arguments, the program starts in server mode and listens for
|
||||
incoming connections on port 8888. When run with the \fBclient\fR argument, the
|
||||
program connects to a server running on localhost.
|
||||
|
||||
.LP
|
||||
Messages are sent as buffered plain text with minimal processing, if any.
|
||||
The interface is text-based and uses the ncurses library to display a scrolling
|
||||
chat log and an input area. Messages entered by the user are sent over the
|
||||
network and recorded in a local log file.
|
||||
|
||||
.LP
|
||||
All messages are transmitted as plain text with minimal processing.
|
||||
|
||||
.SH MODES
|
||||
.TP
|
||||
.B Server Mode
|
||||
Default mode. Starts a server and waits for a client to connect. Messages are
|
||||
logged to \fBserver.txt\fR.
|
||||
|
||||
.TP
|
||||
.B Client Mode
|
||||
Activated by running \fBct client\fR. Connects to a server at 127.0.0.1 on port
|
||||
8888. Messages are logged to \fBclient.txt\fR.
|
||||
|
||||
.SH COMMANDS
|
||||
.TP
|
||||
.B /quit
|
||||
Exit the program.
|
||||
|
||||
.SH FILES
|
||||
.TP
|
||||
.B server.txt
|
||||
Log file used in server mode.
|
||||
|
||||
.TP
|
||||
.B client.txt
|
||||
Log file used in client mode.
|
||||
|
||||
.SH NOTES
|
||||
.LP
|
||||
This program currently supports a single client-server connection.
|
||||
|
||||
.LP
|
||||
The program must be run in two separate instances: one as server and one as
|
||||
client.
|
||||
|
||||
|
||||
6
disp.cc
6
disp.cc
@@ -24,7 +24,7 @@ clearRows(int startingRow, int endingRow)
|
||||
|
||||
// display a file using ncurses
|
||||
int
|
||||
displayFile(string path, int startLineNum = 0, int numLines = 10)
|
||||
displayFile(string path, int startLineNum = 0, int numLines = height - 10)
|
||||
{
|
||||
ifstream file(path);
|
||||
if (!file) {
|
||||
@@ -41,14 +41,14 @@ displayFile(string path, int startLineNum = 0, int numLines = 10)
|
||||
// line number isn't too high
|
||||
{
|
||||
if (num >= startLineNum) {
|
||||
move(lineNum, 0);
|
||||
move(lineNum, 2);
|
||||
printw("%s", line.c_str());
|
||||
lineNum++; // increment the row number after
|
||||
// printing each line
|
||||
}
|
||||
num++;
|
||||
}
|
||||
move(DEFAULT_CUR_Y, DEFAULT_CUR_X);
|
||||
movetobox();
|
||||
refresh();
|
||||
return 0;
|
||||
}
|
||||
|
||||
149
main.cc
149
main.cc
@@ -13,7 +13,8 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector> //try to make the program work without this without doing anything awful but do it later
|
||||
// #include <vector> //try to make the program work without this without doing anything awful but do
|
||||
// it later
|
||||
|
||||
#include "client.h"
|
||||
#include "disp.h"
|
||||
@@ -23,13 +24,14 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
const int PORT_NUM = 8888;
|
||||
string IP_ADDRESS = "127.0.0.1";
|
||||
int PORT_NUM = 8888;
|
||||
string IP_ADDRESS = "0.0.0.0";
|
||||
const int DEFAULT_CUR_X = 2; // the x position of the preferred default cursor
|
||||
// position for message entry
|
||||
const int DEFAULT_CUR_Y = 12; // the x position of the preferred default cursor
|
||||
// position for message entry
|
||||
|
||||
int width, height;
|
||||
chatmode_t mode = NO_MODE; // what mode is this program in. 0 = nothing. 1 = server. 2 = client
|
||||
|
||||
int serverSocketDescriptor; // a global variable for storing the server socket
|
||||
@@ -37,8 +39,8 @@ int serverSocketDescriptor; // a global variable for storing the server socket
|
||||
int clientSocketDescriptor;
|
||||
|
||||
// This has not been used:
|
||||
//vector<int> clientSocketDescriptors; // a global vector for storing client
|
||||
// socket descriptors
|
||||
// vector<int> clientSocketDescriptors; // a global vector for storing client
|
||||
// socket descriptors
|
||||
|
||||
// keep track of the session time using global variables
|
||||
struct timeval start1, end1;
|
||||
@@ -60,15 +62,53 @@ struct waitClientArgs {
|
||||
// struct to keep track of
|
||||
};
|
||||
|
||||
void usage(char *progname, bool *m);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
if (argc > 1 && (!strncmp(argv[1], "client", 6))) {
|
||||
/*
|
||||
* ch: getopt
|
||||
* exit: set to true when infinite loop is to end
|
||||
* userInput: character buffer for the user's next message
|
||||
* modeless: check whether a modeless exit was accidental
|
||||
*/
|
||||
int ch, inlen;
|
||||
bool exit = false;
|
||||
char *userInput = new char[1024];
|
||||
char *userInputF = new char[1032];
|
||||
bool modeless = false;
|
||||
|
||||
if (argc == 1) {
|
||||
usage(argv[0], &modeless);
|
||||
goto leave;
|
||||
}
|
||||
|
||||
while ((ch = getopt(argc, argv, "m:a:p:h")) != -1)
|
||||
switch (ch) {
|
||||
case 'm':
|
||||
if (!strncmp(optarg, "client", 6)) {
|
||||
logFileName = "client.txt";
|
||||
mode = CLIENT_MODE;
|
||||
} else {
|
||||
} else if (!strncmp(optarg, "server", 6)) {
|
||||
logFileName = "server.txt";
|
||||
mode = SERVER_MODE;
|
||||
} else {
|
||||
usage(argv[0], &modeless);
|
||||
goto leave;
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
IP_ADDRESS = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
PORT_NUM = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
usage(argv[0], &modeless);
|
||||
goto leave;
|
||||
break;
|
||||
}
|
||||
|
||||
// putting this lower down to circumvent the terminal brick when the
|
||||
@@ -85,17 +125,14 @@ main(int argc, char *argv[])
|
||||
// there was already content in the log file
|
||||
linePos = linesInFile(logFileName) - LOG_LENGTH + 1;
|
||||
|
||||
char *userInput = new char[1024];
|
||||
bool exit = false;
|
||||
|
||||
switch (mode) {
|
||||
case CLIENT_MODE:
|
||||
writeToFile(logFileName, "CLIENT MODE");
|
||||
writeToFile(logFileName, "SYSTEM: CLIENT MODE");
|
||||
setupClient();
|
||||
linePos++;
|
||||
break;
|
||||
case SERVER_MODE:
|
||||
writeToFile(logFileName, "SERVER MODE");
|
||||
writeToFile(logFileName, "SYSTEM: SERVER MODE");
|
||||
setupServer(PORT_NUM);
|
||||
linePos++;
|
||||
break;
|
||||
@@ -105,6 +142,34 @@ main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
while (!exit) {
|
||||
clear();
|
||||
|
||||
// Draw outer chat border
|
||||
box(stdscr, 0, 0);
|
||||
|
||||
// Get screen size
|
||||
// int height, width;
|
||||
getmaxyx(stdscr, height, width);
|
||||
|
||||
// Draw separator line above input
|
||||
mvhline(height - 3, 1, 0, width - 2);
|
||||
|
||||
// Labels
|
||||
mvprintw(0, 2, " Chat ");
|
||||
// mvprintw(height - 3, 2, " Input ");
|
||||
switch (mode) {
|
||||
case CLIENT_MODE:
|
||||
mvprintw(height - 3, 2, " Client ");
|
||||
break;
|
||||
case SERVER_MODE:
|
||||
mvprintw(height - 3, 2, " Server ");
|
||||
break;
|
||||
default:
|
||||
mvprintw(height - 3, 2, " Input ");
|
||||
break;
|
||||
}
|
||||
|
||||
// Display Chat Log
|
||||
displayFile(logFileName, linePos, LOG_LENGTH);
|
||||
|
||||
// scroll along the screen if and when required so that it stays
|
||||
@@ -112,21 +177,36 @@ main(int argc, char *argv[])
|
||||
if (linesInFile(logFileName) > LOG_LENGTH)
|
||||
linePos++;
|
||||
|
||||
/* clear message box / reset cursor */
|
||||
move(12, 0);
|
||||
printw(">\t\t\t");
|
||||
move(12, 2);
|
||||
// Input area
|
||||
move(height - 2, 2);
|
||||
clrtoeol();
|
||||
printw("You: ");
|
||||
movetobox();
|
||||
|
||||
refresh();
|
||||
|
||||
getstr(userInput);
|
||||
writeToFile(logFileName, userInput);
|
||||
|
||||
if (!strncmp(userInput, "/quit", 5))
|
||||
exit = true;
|
||||
|
||||
if (mode == 1)
|
||||
send(clientSocketDescriptor, (char *)userInput, strlen(userInput), 0);
|
||||
else
|
||||
send(clientSocketDescriptor, (char *)userInput, strlen(userInput), 0);
|
||||
inlen = strlen(userInput);
|
||||
|
||||
switch (mode) {
|
||||
case SERVER_MODE:
|
||||
snprintf(userInputF, 1032 * sizeof(char), "Server: %s", userInput);
|
||||
send(clientSocketDescriptor, (char *)userInput, inlen, 0);
|
||||
break;
|
||||
case CLIENT_MODE:
|
||||
snprintf(userInputF, 1032 * sizeof(char), "Client: %s", userInput);
|
||||
send(clientSocketDescriptor, (char *)userInput, inlen, 0);
|
||||
break;
|
||||
default:
|
||||
goto leave;
|
||||
break;
|
||||
}
|
||||
|
||||
writeToFile(logFileName, userInputF);
|
||||
}
|
||||
|
||||
leave:
|
||||
@@ -141,11 +221,34 @@ leave:
|
||||
closeServer();
|
||||
break;
|
||||
default:
|
||||
puts("Warn: program appears to have successfully finished without ever setting "
|
||||
"mode.");
|
||||
if (!modeless) {
|
||||
puts("Warn: program appears to have successfully finished without ever "
|
||||
"setting mode.");
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* remember to call 'goto leave' after running */
|
||||
void
|
||||
usage(char *progname, bool *m) // m:a:p:h
|
||||
{
|
||||
printf("\x1b[1mUsage:\x1b[0m\n"
|
||||
" %s client\t\x1b[3m# run as client\x1b[0m\n"
|
||||
" %s server\t\x1b[3m# run as server\x1b[0m\n"
|
||||
"\n\n"
|
||||
"\x1b[1mOptions:\x1b[0m\n"
|
||||
" \x1b[1m-m [client|server]\x1b[0m\tMode \x1b[1;31m(required)\x1b[0m\n"
|
||||
"\n"
|
||||
" \x1b[1m-a [255.255.255.255]\x1b[0m\tAddress\n"
|
||||
" \t\t\tserver mode: listen address \x1b[33m(default: 0.0.0.0)\x1b[0m\n"
|
||||
" \t\t\tclient mode: address of server \x1b[33m(default: 127.0.0.1)\x1b[0m\n"
|
||||
"\n"
|
||||
" \x1b[1m-p [0-65535]\x1b[0m\t\tPort number\n",
|
||||
progname, progname);
|
||||
*m = true;
|
||||
}
|
||||
|
||||
6
public.h
6
public.h
@@ -7,8 +7,9 @@ using namespace std;
|
||||
|
||||
typedef enum { NO_MODE = 0, SERVER_MODE = 1, CLIENT_MODE = 2 } chatmode_t;
|
||||
|
||||
extern int width, height;
|
||||
extern chatmode_t mode;
|
||||
extern const int PORT_NUM;
|
||||
extern int PORT_NUM;
|
||||
extern string IP_ADDRESS;
|
||||
extern const int DEFAULT_CUR_X;
|
||||
extern const int DEFAULT_CUR_Y;
|
||||
@@ -27,4 +28,7 @@ void *waitForClient(void *argss);
|
||||
void *pollForClient();
|
||||
void *pollForSever(void *args);
|
||||
|
||||
#define movetobox() \
|
||||
move(height - 2, 7)
|
||||
|
||||
#endif
|
||||
|
||||
21
server.cc
21
server.cc
@@ -26,7 +26,7 @@ waitForClient(void *argss)
|
||||
clientSocketDescriptor =
|
||||
accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr, &args->newSockAddrSize);
|
||||
if (clientSocketDescriptor >= 0) {
|
||||
writeToFile(logFileName, "client connected");
|
||||
writeToFile(logFileName, "SYSTEM: Client connected.");
|
||||
if (linesInFile(logFileName) > LOG_LENGTH)
|
||||
linePos++;
|
||||
displayFile(logFileName, linePos, LOG_LENGTH);
|
||||
@@ -39,17 +39,22 @@ waitForClient(void *argss)
|
||||
void *
|
||||
pollForClient()
|
||||
{
|
||||
char msg[1024];
|
||||
int b;
|
||||
char msg[1024], msgf[1033];
|
||||
|
||||
while (1) {
|
||||
client_message_loop:
|
||||
// receive a message from the client (listen)
|
||||
memset(&msg, 0, sizeof(msg)); // clear the buffer
|
||||
bytesRead += recv(clientSocketDescriptor, (char *)&msg, sizeof(msg), 0);
|
||||
/* receive a message from the client (listen) */
|
||||
memset(&msg, 0, sizeof(msg)); /* clear buffer */
|
||||
b = recv(clientSocketDescriptor, (char *)&msg, sizeof(msg) - 1, 0);
|
||||
msg[b] = '\0';
|
||||
bytesRead += b;
|
||||
|
||||
if (msg[0] == '\0')
|
||||
goto client_message_loop;
|
||||
|
||||
writeToFile(logFileName, msg);
|
||||
snprintf(msgf, sizeof(msgf), "Client: %s", msg);
|
||||
writeToFile(logFileName, msgf);
|
||||
|
||||
if (linesInFile(logFileName) > LOG_LENGTH)
|
||||
linePos++;
|
||||
@@ -91,7 +96,7 @@ setupServer(int port)
|
||||
fprintf(stderr, "Error binding socket to local address!\n");
|
||||
exit(0);
|
||||
}
|
||||
writeToFile(logFileName, "Waiting for a client to connect...");
|
||||
writeToFile(logFileName, "SYSTEM: Waiting for a client to connect...");
|
||||
// listen for up to 5 requests at a time
|
||||
listen(serverSocketDescriptor, 5);
|
||||
// receive a request from client using accept
|
||||
@@ -105,7 +110,7 @@ setupServer(int port)
|
||||
aaa->newSockAddrSize = newSockAddrSize;
|
||||
int rc = pthread_create(&client_wait_thread, nullptr, waitForClient, aaa);
|
||||
pthread_detach(client_wait_thread);
|
||||
writeToFile(logFileName, "Server started successfully");
|
||||
writeToFile(logFileName, "SYSTEM: Server started successfully.");
|
||||
gettimeofday(&start1, NULL);
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user