23 changed files with 914 additions and 632 deletions

No files matched your search

-1
View File
@@ -3,7 +3,6 @@
"UseTab": "Always", "UseTab": "Always",
"IndentWidth": 8, "IndentWidth": 8,
"TabWidth": 8, "TabWidth": 8,
"ColumnLimit": 100,
"LineEnding": "LF", "LineEnding": "LF",
"RemoveBracesLLVM": true, "RemoveBracesLLVM": true,
"AlwaysBreakAfterReturnType": "AllDefinitions", "AlwaysBreakAfterReturnType": "AllDefinitions",
+3 -4
View File
@@ -1,4 +1,3 @@
client.txt old/client.txt
server.txt old/server.txt
*.o old/main
ct
-18
View File
@@ -1,18 +0,0 @@
# Compiler and flags
TARGET = ct
CXX = g++
CXXFLAGS = -pthread -Wall -Wextra -O2 -std=c++17 -lncurses -g
SRC = $(wildcard *.cc)
OBJ = $(SRC:.cc=.o)
all: $(TARGET)
clean:
rm -f $(TARGET) *.o
$(TARGET): $(OBJ)
$(CXX) $(OBJ) $(CXXFLAGS) -o $(TARGET)
%.o: %.cc
$(CXX) -c $< $(CXXFLAGS) -o $@
-42
View File
@@ -1,42 +0,0 @@
# threaded network chat refactor
I have split functions semantically into different files and tied all global variables together in
`public.h` so they work without modifications.
Additional changes:
- Fixed the dreaded bug where you have to launch the program twice to make it work
- Adjusted the ncurses stuff a bit so it doesn't look as janky
- Changed server/client mode (`mode` variable) from an integer to an enum (still works as an integer)
## What now?
See TODO. From here we're going to push the code to the gitlab like we were told to after everyone
is set up. Follow the coding style guidelines because otherwise everything will be extremely gross
and inconsistent. If you set up your editor correctly, it will take care of most of that stuff.
## 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
- Decide who gets what responsibilities
- Distribute appropriate code to people with those responsibilities
- Have everyone upload their respective code to their sections of the gitlab
## Build
```
make
```
## Usage
```
./ct # run as server
```
```
./ct client # run as client
```
+5 -4
View File
@@ -37,9 +37,9 @@ I recommend skimming over these for inspiration.
- return type goes on its own line, - return type goes on its own line,
- function name() is on a line below that, and - function name() is on a line below that, and
- open bracket `{` after that. - open bracket `{` after that.
- no brackets on `for`, `if`, `while`, etc. statements that only contain one line - `for`, `if`, `while`, etc. statements that only contain one line
- Use `/* comments */` for permanent comments, `// comments` for temporary ones e.g. `TODO`'s or notices - Use `/* comments */` for permanent comments, `// comments` for temporary ones e.g. `TODO`'s or notices
- **Try to make every line 100 characters wide or less.** - **Try to make every line 120 characters wide or less.**
- Start source files as `.cc` straight away, even if they're pure **C**. - Start source files as `.cc` straight away, even if they're pure **C**.
### C++-specific guidelines ### C++-specific guidelines
@@ -60,7 +60,7 @@ The length of a function name, variable name, et cetera should be directly propo
#### Don't #### Don't
- abbreviate global variables **EVER**, - abbreviate global variables,
- mention the data type in a variable name, - mention the data type in a variable name,
- use a full word where an abbreviation will do - use a full word where an abbreviation will do
- (especially not in a variable that will die 5 lines later), - (especially not in a variable that will die 5 lines later),
@@ -70,7 +70,7 @@ The length of a function name, variable name, et cetera should be directly propo
#### Do #### Do
- abbreviate extremely short-lived variables to one letter, - abbreviate extremely short-lived variables to one letter,
- e.g. `for (int index = 0; index < 10; i++)` -> `for (int i = 0; i < 10; i++)` - e.g. `for (int index=0, index<10, i++)` -> `for (int i=0, i<10, i++)`
- break any rule if following it ruins the readability - break any rule if following it ruins the readability
## Source File layout ## Source File layout
@@ -120,3 +120,4 @@ div_numb(int n, int d)
} }
``` ```
-103
View File
@@ -1,103 +0,0 @@
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include "disp.h"
#include "log.h"
#include "public.h"
struct waitClientArgs {
sockaddr_in newSockAddr;
socklen_t newSockAddrSize;
int auxInt; // used in client mode because I didn't want to make another
// struct to keep track of
};
void
closeClient()
{
long e = end1.tv_sec - start1.tv_sec;
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
puts("********Session********");
printf("Bytes written: %i\n", bytesWritten);
printf("Bytes read: %i\n", bytesRead);
printf("Elapsed time: %ld secs\n", e);
puts("Connection closed.");
}
void *
pollForSever(void *args)
{
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");
if (msg[0] == '\0')
goto server_message_loop;
writeToFile(logFileName, msg);
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
return nullptr;
}
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
struct hostent *host = gethostbyname(IP_ADDRESS.c_str());
sockaddr_in sendSockAddr;
bzero((char *)&sendSockAddr, sizeof(sendSockAddr));
sendSockAddr.sin_family = AF_INET;
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...
int status =
connect(clientSocketDescriptor, (sockaddr *)&sendSockAddr, sizeof(sendSockAddr));
if (status < 0)
writeToFile(logFileName, "Error connecting to socket!");
writeToFile(logFileName, "Connected to the server!");
int bytesRead, bytesWritten = 0;
struct timeval start1, end1;
gettimeofday(&start1, NULL);
auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->auxInt = clientSocketDescriptor;
int rc = pthread_create(&client_wait_thread, nullptr, pollForSever, aaa);
pthread_detach(client_wait_thread);
}
-8
View File
@@ -1,8 +0,0 @@
#ifndef CLIENT_H
#define CLIENT_H
void closeClient();
void *pollForServer(void *args);
void setupClient();
#endif
-55
View File
@@ -1,55 +0,0 @@
#include <fstream>
#include <curses.h>
#include "disp.h"
#include "public.h"
// clears ncurses rows in a specific region only
void
clearRows(int startingRow, int endingRow)
{
// preserve the cursor location
int yBefore, xBefore;
getyx(stdscr, yBefore, xBefore);
for (int i = startingRow; i < endingRow; i++) {
move(i, 0);
clrtoeol(); // clear to end of line
}
// restore original cursor location
move(yBefore, xBefore);
}
// display a file using ncurses
int
displayFile(string path, int startLineNum = 0, int numLines = 10)
{
ifstream file(path);
if (!file) {
return 1;
} else {
clearRows(0, numLines + 1); // clear the chat area
int lineNum = 0;
string line;
// print each line directly to the screen
int num = 0;
while (getline(file, line) &&
num <= numLines + startLineNum + 1) // while there is file content and the
// line number isn't too high
{
if (num >= startLineNum) {
move(lineNum, 0);
printw("%s", line.c_str());
lineNum++; // increment the row number after
// printing each line
}
num++;
}
move(DEFAULT_CUR_Y, DEFAULT_CUR_X);
refresh();
return 0;
}
}
-11
View File
@@ -1,11 +0,0 @@
#ifndef DISP_H
#define DISP_H
#include <string>
using namespace std;
void clearRows(int startingRow, int endingRow);
int displayFile(string path, int startLineNum, int numLines);
#endif
File renamed without changes.
@@ -3,7 +3,6 @@
"UseTab": "Always", "UseTab": "Always",
"IndentWidth": 8, "IndentWidth": 8,
"TabWidth": 8, "TabWidth": 8,
"ColumnLimit": 120,
"LineEnding": "LF", "LineEnding": "LF",
"RemoveBracesLLVM": true, "RemoveBracesLLVM": true,
"AlwaysBreakAfterReturnType": "AllDefinitions", "AlwaysBreakAfterReturnType": "AllDefinitions",
@@ -4,7 +4,6 @@ BasedOnStyle: LLVM # defaults
UseTab: Always UseTab: Always
IndentWidth: 8 IndentWidth: 8
TabWidth: 8 TabWidth: 8
ColumnLimit: 120
LineEnding: LF # unix LineEnding: LF # unix
RemoveBracesLLVM: true # single-statement blocks RemoveBracesLLVM: true # single-statement blocks
-56
View File
@@ -1,56 +0,0 @@
#include <sys/stat.h>
#include <fstream>
#include <ios>
#include "log.h"
#include "public.h"
// gets the number of lines in a file. returns -1 if there was an error.
int
linesInFile(string path)
{
ifstream file(path);
if (!file) {
return 1;
} else {
int lineNum = 0;
string line;
int num = 0;
while (getline(file, line))
num++;
return num;
}
}
// appends a line of text to the end of a given file. returns 0 if the file
// existed, 1 if it didn't work
int
writeToFile(string path, string line, bool incLineNum)
{
ofstream file;
struct stat s;
/*
* It APPEARS this fixes our problem where the program has to be run twice to work correctly.
*/
if (stat(path.c_str(), &s) != 0) {
file.open(path, ios_base::out);
file << "SYSTEM: No log file found. Created a new one." << endl;
file.close();
linePos += 5; /* This seems to be what actually fixes everything. Not sure. */
}
file.open(path, ios_base::app);
if (file.is_open()) {
file << line << endl;
file.close();
//linePos++ // We might also want this here at some point?
return 0;
} else {
// do something if it didn't work
return 1; // i guess that's good enough for now
}
}
-9
View File
@@ -1,9 +0,0 @@
#ifndef LOG_H
#define LOG_H
#include "public.h"
int linesInFile(string path);
int writeToFile(string path, string line, bool incLineNum);
#endif
-149
View File
@@ -1,149 +0,0 @@
#include <arpa/inet.h>
#include <curses.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string>
#include <vector> //try to make the program work without this without doing anything awful but do it later
#include "client.h"
#include "disp.h"
#include "log.h"
#include "public.h"
#include "server.h"
using namespace std;
const int PORT_NUM = 8888;
string IP_ADDRESS = "127.0.0.1";
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
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
// descriptor.
int clientSocketDescriptor;
vector<int> clientSocketDescriptors; // a global vector for storing client
// socket descriptors
// keep track of the session time using global variables
struct timeval start1, end1;
// also keep track of the amount of data sent as well
int bytesRead, bytesWritten = 0;
const int LOG_LENGTH = 10; // number of text log file lines to display
int linePos = 0; // counter for what current position to use in the file
string logFileName; // the name of the log file
pthread_t client_wait_thread;
struct waitClientArgs {
sockaddr_in newSockAddr;
socklen_t newSockAddrSize;
int auxInt; // used in client mode because I didn't want to make another
// struct to keep track of
};
int
main(int argc, char *argv[])
{
if (argc > 1 && (!strncmp(argv[1], "client", 6))) {
logFileName = "client.txt";
mode = CLIENT_MODE;
} else {
logFileName = "server.txt";
mode = SERVER_MODE;
}
// putting this lower down to circumvent the terminal brick when the
// socket was already in use results in all network functonality no
// longer working.
initscr(); // creates stdscr. important step
use_default_colors(); // this apparently tells it to use default
// terminal colors whenever there is no color
// attribute applied
// printw("Starting server...");
// you have to do this to make the scrolling still work correctly if
// 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");
setupClient();
linePos++;
break;
case SERVER_MODE:
writeToFile(logFileName, "SERVER MODE");
setupServer(PORT_NUM);
linePos++;
break;
default:
goto leave;
break;
}
while (!exit) {
displayFile(logFileName, linePos, LOG_LENGTH);
// scroll along the screen if and when required so that it stays
// in sync
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
/* clear message box / reset cursor */
move(12, 0);
printw(">\t\t\t");
move(12, 2);
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);
}
leave:
endwin();
// closeServer();
switch (mode) {
case SERVER_MODE:
closeServer();
break;
case CLIENT_MODE:
closeServer();
break;
default:
puts("Warn: program appears to have successfully finished without ever setting "
"mode.");
return 1;
break;
}
return 0;
}
+400
View File
@@ -0,0 +1,400 @@
/*an attempt at pthread and network and ncurses all in the same function
*/
#include <arpa/inet.h>
#include <curses.h>
#include <fstream>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <ostream>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector> //try to make the program work without this without doing anything awful but do it later
using namespace std;
const int PORT_NUM = 8888;
const string IP_ADDRESS = "127.0.0.1";
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 mode = 0; // what mode is this program in. 0 = nothing. 1 = server. 2 = client
int serverSocketDescriptor; // a global variable for storing the server socket
// descriptor.
int clientSocketDescriptor;
vector<int> clientSocketDescriptors; // a global vector for storing client
// socket descriptors
// keep track of the session time using global variables
struct timeval start1, end1;
// also keep track of the amount of data sent as well
int bytesRead, bytesWritten = 0;
const int LOG_LENGTH = 10; // number of text log file lines to display
int linePos = 0; // counter for what current position to use in the file
string logFileName; // the name of the log file
pthread_t client_wait_thread;
int writeToFile(string path, string line, bool incLineNum = true);
void *waitForClient(void *argss);
void *pollForClient();
void *pollForSever(void *args);
// clears ncurses rows in a specific region only
void
clearRows(int startingRow, int endingRow)
{
// preserve the cursor location
int yBefore, xBefore;
getyx(stdscr, yBefore, xBefore);
for (int i = startingRow; i < endingRow; i++) {
move(i, 0);
clrtoeol(); // clear to end of line
}
// restore original cursor location
move(yBefore, xBefore);
}
// display a file using ncurses
int
displayFile(string path, int startLineNum = 0, int numLines = 10)
{
ifstream file(path);
if (!file) {
return 1;
} else {
clearRows(0, numLines + 1); // clear the chat area
int lineNum = 0;
string line;
// print each line directly to the screen
int num = 0;
while (getline(file, line) &&
num <= numLines + startLineNum +
1) // while there is file content and the line
// number isn't too high
{
if (num >= startLineNum) {
move(lineNum, 0);
printw("%s", line.c_str());
lineNum++; // increment the row number after
// printing each line
}
num++;
}
move(DEFAULT_CUR_Y, DEFAULT_CUR_X);
refresh();
return 0;
}
}
// gets the number of lines in a file. returns -1 if there was an error.
int
linesInFile(string path)
{
ifstream file(path);
if (!file) {
return 1;
} else {
int lineNum = 0;
string line;
int num = 0;
while (getline(file, line))
num++;
return num;
}
}
struct waitClientArgs {
sockaddr_in newSockAddr;
socklen_t newSockAddrSize;
int auxInt; // used in client mode because I didn't want to make another
// struct to keep track of
};
void *
waitForClient(void *argss)
{
waitClientArgs *args = static_cast<waitClientArgs *>(argss);
clientSocketDescriptor =
accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr,
&args->newSockAddrSize);
if (clientSocketDescriptor >= 0) {
writeToFile(logFileName, "client connected");
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
delete args; // delete to avoid memory leaks
return pollForClient();
}
void *
pollForClient()
{
char msg[1024];
while (1) {
// receive a message from the client (listen)
memset(&msg, 0, sizeof(msg)); // clear the buffer
bytesRead +=
recv(clientSocketDescriptor, (char *)&msg, sizeof(msg), 0);
writeToFile(logFileName,
msg); // write the client's message to file
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
return nullptr;
}
// set up a suitable server for this
int
setupServer(int port)
{
// setup a socket and connection tools
sockaddr_in servAddr;
bzero((char *)&servAddr, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(port);
// open stream oriented socket with internet address
// also keep track of the socket descriptor
serverSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocketDescriptor < 0) {
// keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error establishing the server socket!\n");
exit(0);
}
// bind the socket to its local address
int bindStatus = bind(serverSocketDescriptor,
(struct sockaddr *)&servAddr, sizeof(servAddr));
if (bindStatus < 0) {
// keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error binding socket to local address!\n");
exit(0);
}
writeToFile(logFileName, "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
// we need a new address to connect with the client
sockaddr_in newSockAddr;
socklen_t newSockAddrSize = sizeof(newSockAddr);
// accept, create a new socket descriptor to
// handle the new connection with client
auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->newSockAddr = newSockAddr;
aaa->newSockAddrSize = newSockAddrSize;
int rc =
pthread_create(&client_wait_thread, nullptr, waitForClient, aaa);
pthread_detach(client_wait_thread);
writeToFile(logFileName, "Server started sucessfully");
gettimeofday(&start1, NULL);
return 0;
}
void
closeServer()
{
long e = end1.tv_sec - start1.tv_sec; /* the linter freaks out if you
don't save it as a variable */
// we need to close the socket descriptors after we're all done
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
close(serverSocketDescriptor);
/* Don't just die silently */
printf("********Session********\n");
printf("Bytes written: %i\nBytes Read: %i\n", bytesWritten, bytesRead);
printf("Elapsed time: %ld\n secs\n", e);
printf("Connection closed...\n");
}
void
closeClient()
{
long e = end1.tv_sec - start1.tv_sec;
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
printf("********Session********");
printf("Bytes written: %i\nBytes read: %i\n", bytesWritten, bytesRead);
printf("Elapsed time: %ld\n secs\n", e);
printf("Connection closed...\n");
}
void *
pollForSever(void *args)
{
waitClientArgs *aaa = static_cast<waitClientArgs *>(args);
int socketDescriptor = (int)aaa->auxInt;
char msg[1024];
while (1) {
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");
writeToFile(logFileName, msg);
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
}
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
struct hostent *host = gethostbyname(IP_ADDRESS.c_str());
sockaddr_in sendSockAddr;
bzero((char *)&sendSockAddr, sizeof(sendSockAddr));
sendSockAddr.sin_family = AF_INET;
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...
int status = connect(clientSocketDescriptor, (sockaddr *)&sendSockAddr,
sizeof(sendSockAddr));
if (status < 0)
writeToFile(logFileName, "Error connecting to socket!");
writeToFile(logFileName, "Connected to the server!");
int bytesRead, bytesWritten = 0;
struct timeval start1, end1;
gettimeofday(&start1, NULL);
auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->auxInt = clientSocketDescriptor;
int rc =
pthread_create(&client_wait_thread, nullptr, pollForSever, aaa);
pthread_detach(client_wait_thread);
}
// appends a line of text to the end of a given file. returns 0 if the file
// existed, 1 if it didn't work
int
writeToFile(string path, string line, bool incLineNum)
{
ofstream file;
file.open(path, ios_base::app); // open the file in append mode
if (file.is_open()) {
file << line << endl;
// this probably would help but theres too much broken stuff
// right now to be sure if (incLineNum)
//{
// linePos++;
// }
return 0;
} else {
// do something if it didn't work
return 1; // i guess that's good enough for now
}
}
int
main(int argc, char *argv[])
{
if (argc > 1 && argv[1][0] == 'c') {
// client mode
logFileName = "client.txt";
mode = 2;
} else {
// server mode
logFileName = "server.txt";
mode = 1;
}
// putting this lower down to circumvent the terminal brick when the
// socket was already in use results in all network functonality no
// longer working.
initscr(); // creates stdscr. important step
use_default_colors(); // this apparently tells it to use default
// terminal colors whenever there is no color
// attribute applied
// printw("Starting server...");
// you have to do this to make the scrolling still work correctly if
// there was already content in the log file
linePos = linesInFile(logFileName) - LOG_LENGTH + 1;
char *userInput = new char[1024];
bool exit = false;
if (mode == 2) {
writeToFile(logFileName, "CLIENT MODE");
setupClient();
} else if (mode == 1) {
writeToFile(logFileName, "SERVER MODE");
setupServer(PORT_NUM);
}
while (!exit) {
displayFile(logFileName, linePos, LOG_LENGTH);
// scroll along the screen if and when required so that it stays
// in sync
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
move(12, 0);
printw("> ");
getstr(userInput);
writeToFile(logFileName, userInput);
if (mode == 1) {
send(clientSocketDescriptor, (char *)userInput,
strlen(userInput), 0);
} else {
send(clientSocketDescriptor, (char *)userInput,
strlen(userInput), 0);
}
}
endwin();
closeServer();
return 0;
}
+15
View File
@@ -0,0 +1,15 @@
# Compiler and flags
CXX = g++
CXXFLAGS = -pthread -Wall -Wextra -O2 -std=c++17 -lcurses
# Targets
all: main
# Client build
main: main.cpp
$(CXX) $(CXXFLAGS) main.cpp -o main
# Clean build artifacts
clean:
rm -f main
+3
View File
@@ -0,0 +1,3 @@
an attempt at a threaded ncurses network chat in c++
the biggest bug right now is that if you start the server without having started it previously and there isn't a server.txt and client.txt from a previous session, it doesn't work at all. It will work when you try it a second time.
+86
View File
@@ -0,0 +1,86 @@
/*this network test uses more complicated network functionality but doesnt use threads or ncurses.
that means you have to wait for the other end to send a message before getting to write a new message
to run, do ./client.cpp 127.0.0.1 8080
this one has args
*/
#include <iostream>
#include <string>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <fstream>
using namespace std;
//Client side
int main(int argc, char *argv[])
{
//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
char *serverIp = argv[1]; int port = atoi(argv[2]);
//create a message buffer
char msg[1500];
//setup a socket and connection tools
struct hostent* host = gethostbyname(serverIp);
sockaddr_in sendSockAddr;
bzero((char*)&sendSockAddr, sizeof(sendSockAddr));
sendSockAddr.sin_family = AF_INET;
sendSockAddr.sin_addr.s_addr =
inet_addr(inet_ntoa(*(struct in_addr*)*host->h_addr_list));
sendSockAddr.sin_port = htons(port);
int clientSd = socket(AF_INET, SOCK_STREAM, 0);
//try to connect...
int status = connect(clientSd,
(sockaddr*) &sendSockAddr, sizeof(sendSockAddr));
if(status < 0)
{
cout<<"Error connecting to socket!"<<endl;// break;
}
cout << "Connected to the server!" << endl;
int bytesRead, bytesWritten = 0;
struct timeval start1, end1;
gettimeofday(&start1, NULL);
while(1)
{
cout << ">";
string data;
getline(cin, data);
memset(&msg, 0, sizeof(msg));//clear the buffer
strcpy(msg, data.c_str());
if(data == "exit")
{
send(clientSd, (char*)&msg, strlen(msg), 0);
break;
}
bytesWritten += send(clientSd, (char*)&msg, strlen(msg), 0);
cout << "Awaiting server response..." << endl;
memset(&msg, 0, sizeof(msg));//clear the buffer
bytesRead += recv(clientSd, (char*)&msg, sizeof(msg), 0);
if(!strcmp(msg, "exit"))
{
cout << "Server has quit the session" << endl;
break;
}
cout << "Server: " << msg << endl;
}
gettimeofday(&end1, NULL);
close(clientSd);
cout << "********Session********" << endl;
cout << "Bytes written: " << bytesWritten <<
" Bytes read: " << bytesRead << endl;
cout << "Elapsed time: " << (end1.tv_sec- start1.tv_sec)
<< " secs" << endl;
cout << "Connection closed" << endl;
return 0;
}
+402
View File
@@ -0,0 +1,402 @@
/*an attempt at pthread and network and ncurses all in the same function
*/
#include <iostream>
#include <ostream>
#include <string>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <curses.h>
#include <fstream>
#include <pthread.h>
#include <vector> //try to make the program work without this without doing anything awful but do it later
using namespace std;
const int PORT_NUM = 8888;
const string IP_ADDRESS = "127.0.0.1";
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 mode = 0; //what mode is this program in. 0 = nothing. 1 = server. 2 = client
int serverSocketDescriptor;//a global variable for storing the server socket descriptor.
int clientSocketDescriptor;
vector<int> clientSocketDescriptors;//a global vector for storing client socket descriptors
//keep track of the session time using global variables
struct timeval start1, end1;
//also keep track of the amount of data sent as well
int bytesRead, bytesWritten = 0;
const int LOG_LENGTH = 10;//number of text log file lines to display
int linePos = 0; //counter for what current position to use in the file
string logFileName;//the name of the log file
pthread_t client_wait_thread;
int writeToFile(string path, string line, bool incLineNum = true);
void* waitForClient(void* argss);
void* pollForClient();
void* pollForSever(void* args);
//clears ncurses rows in a specific region only
void clearRows(int startingRow, int endingRow)
{
//preserve the cursor location
int yBefore, xBefore;
getyx(stdscr, yBefore, xBefore);
for (int i = startingRow; i < endingRow; i++)
{
move(i, 0);
clrtoeol();//clear to end of line
}
//restore original cursor location
move(yBefore, xBefore);
}
//display a file using ncurses
int displayFile(string path, int startLineNum = 0, int numLines = 10)
{
ifstream file(path);
if (!file)
{
return 1;
}
else
{
clearRows(0, numLines+1); //clear the chat area
int lineNum = 0;
string line;
//print each line directly to the screen
int num = 0;
while(getline(file, line) && num <= numLines + startLineNum + 1) //while there is file content and the line number isn't too high
{
if (num >= startLineNum)
{
move(lineNum, 0);
printw("%s", line.c_str());
lineNum++;//increment the row number after printing each line
}
num++;
}
move(DEFAULT_CUR_Y, DEFAULT_CUR_X);
refresh();
return 0;
}
}
//gets the number of lines in a file. returns -1 if there was an error.
int linesInFile(string path)
{
ifstream file(path);
if (!file)
{
return 1;
}
else
{
int lineNum = 0;
string line;
int num = 0;
while(getline(file, line))
{
num++;
}
return num;
}
}
struct waitClientArgs
{
sockaddr_in newSockAddr;
socklen_t newSockAddrSize;
int auxInt;//used in client mode because I didn't want to make another struct to keep track of
};
void* waitForClient(void* argss)
{
waitClientArgs* args = static_cast<waitClientArgs*>(argss);
clientSocketDescriptor = accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr, &args->newSockAddrSize);
if (clientSocketDescriptor >= 0)
{
writeToFile(logFileName, "client connected");
if (linesInFile(logFileName) > LOG_LENGTH)
{
linePos++;
}
displayFile(logFileName, linePos, LOG_LENGTH);
}
delete args;//delete to avoid memory leaks
return pollForClient();
}
void* pollForClient()
{
char msg[1024];
while(1)
{
//receive a message from the client (listen)
memset(&msg, 0, sizeof(msg));//clear the buffer
bytesRead += recv(clientSocketDescriptor, (char*)&msg, sizeof(msg), 0);
writeToFile(logFileName, msg);//write the client's message to file
if (linesInFile(logFileName) > LOG_LENGTH)
{
linePos++;
}
displayFile(logFileName, linePos, LOG_LENGTH);
}
return nullptr;
}
//set up a suitable server for this
int setupServer(int port)
{
//setup a socket and connection tools
sockaddr_in servAddr;
bzero((char*)&servAddr, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(port);
//open stream oriented socket with internet address
//also keep track of the socket descriptor
serverSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if(serverSocketDescriptor < 0)
{
//keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error establishing the server socket!\n");
exit(0);
}
//bind the socket to its local address
int bindStatus = bind(serverSocketDescriptor, (struct sockaddr*) &servAddr,
sizeof(servAddr));
if(bindStatus < 0)
{
//keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error binding socket to local address!\n");
exit(0);
}
writeToFile(logFileName, "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
//we need a new address to connect with the client
sockaddr_in newSockAddr;
socklen_t newSockAddrSize = sizeof(newSockAddr);
//accept, create a new socket descriptor to
//handle the new connection with client
auto* aaa = new waitClientArgs{};//it looks stupid but it works
aaa->newSockAddr = newSockAddr;
aaa->newSockAddrSize = newSockAddrSize;
int rc = pthread_create(&client_wait_thread, nullptr, waitForClient, aaa);
pthread_detach(client_wait_thread);
writeToFile(logFileName, "Server started sucessfully");
gettimeofday(&start1, NULL);
return 0;
}
void closeServer()
{
long e = end1.tv_sec - start1.tv_sec; /* the linter freaks out if you don't save it as a variable */
//we need to close the socket descriptors after we're all done
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
close(serverSocketDescriptor);
/* Don't just die silently */
printf("********Session********\n");
printf("Bytes written: %i\nBytes Read: %i\n", bytesWritten, bytesRead);
printf("Elapsed time: %ld\n secs\n", e);
printf("Connection closed...\n");
}
void closeClient()
{
long e = end1.tv_sec - start1.tv_sec;
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
printf("********Session********");
printf("Bytes written: %i\nBytes read: %i\n", bytesWritten, bytesRead);
printf("Elapsed time: %ld\n secs\n", e);
printf("Connection closed...\n");
}
void* pollForSever(void* args)
{
waitClientArgs* aaa = static_cast<waitClientArgs*>(args);
int socketDescriptor = (int)aaa->auxInt;
char msg[1024];
while(1)
{
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");
writeToFile(logFileName, msg);
if (linesInFile(logFileName) > LOG_LENGTH)
{
linePos++;
}
displayFile(logFileName, linePos, LOG_LENGTH);
}
}
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
struct hostent* host = gethostbyname(IP_ADDRESS.c_str());
sockaddr_in sendSockAddr;
bzero((char*)&sendSockAddr, sizeof(sendSockAddr));
sendSockAddr.sin_family = AF_INET;
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...
int status = connect(clientSocketDescriptor,
(sockaddr*) &sendSockAddr, sizeof(sendSockAddr));
if(status < 0)
{
writeToFile(logFileName, "Error connecting to socket!");
}
writeToFile(logFileName, "Connected to the server!");
int bytesRead, bytesWritten = 0;
struct timeval start1, end1;
gettimeofday(&start1, NULL);
auto* aaa = new waitClientArgs{};//it looks stupid but it works
aaa->auxInt = clientSocketDescriptor;
int rc = pthread_create(&client_wait_thread, nullptr, pollForSever, aaa);
pthread_detach(client_wait_thread);
}
//appends a line of text to the end of a given file. returns 0 if the file existed, 1 if it didn't work
int writeToFile(string path, string line, bool incLineNum)
{
ofstream file;
file.open(path, ios_base::app);//open the file in append mode
if (file.is_open())
{
file << line << endl;
//this probably would help but theres too much broken stuff right now to be sure
//if (incLineNum)
//{
// linePos++;
//}
return 0;
}
else
{
//do something if it didn't work
return 1;//i guess that's good enough for now
}
}
int main(int argc, char *argv[])
{
if (argc > 1 && argv[1][0] == 'c')
{
//client mode
logFileName = "client.txt";
mode = 2;
}
else
{
//server mode
logFileName = "server.txt";
mode = 1;
}
//putting this lower down to circumvent the terminal brick when the socket was already in use results in all network functonality no longer working.
initscr();//creates stdscr. important step
use_default_colors();//this apparently tells it to use default terminal colors whenever there is no color attribute applied
//printw("Starting server...");
//you have to do this to make the scrolling still work correctly if there was already content in the log file
linePos = linesInFile(logFileName) - LOG_LENGTH + 1;
char *userInput = new char[1024];
bool exit = false;
if (mode == 2)
{
writeToFile(logFileName, "CLIENT MODE");
setupClient();
}
else if (mode == 1)
{
writeToFile(logFileName, "SERVER MODE");
setupServer(PORT_NUM);
}
while (!exit)
{
displayFile(logFileName, linePos, LOG_LENGTH);
//scroll along the screen if and when required so that it stays in sync
if (linesInFile(logFileName) > LOG_LENGTH)
{
linePos++;
}
move(12, 0);
printw("> ");
getstr(userInput);
writeToFile(logFileName, userInput);
if (mode == 1)
{
send(clientSocketDescriptor, (char*)userInput, strlen(userInput), 0);
}
else
{
send(clientSocketDescriptor, (char*)userInput, strlen(userInput), 0);
}
}
endwin();
closeServer();
return 0;
}
-30
View File
@@ -1,30 +0,0 @@
#ifndef PUBLIC_H
#define PUBLIC_H
#include <string>
using namespace std;
typedef enum { NO_MODE = 0, SERVER_MODE = 1, CLIENT_MODE = 2 } chatmode_t;
extern chatmode_t mode;
extern const int PORT_NUM;
extern string IP_ADDRESS;
extern const int DEFAULT_CUR_X;
extern const int DEFAULT_CUR_Y;
extern int serverSocketDescriptor;
extern int clientSocketDescriptor;
extern struct timeval start1, end1;
extern int bytesRead, bytesWritten;
extern const int LOG_LENGTH;
extern int linePos;
extern string logFileName;
extern pthread_t client_wait_thread;
struct waitClientArgs;
int writeToFile(string path, string line, bool incLineNum = true);
void *waitForClient(void *argss);
void *pollForClient();
void *pollForSever(void *args);
#endif
-131
View File
@@ -1,131 +0,0 @@
#include <curses.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include "disp.h"
#include "log.h"
#include "public.h"
#include "server.h"
struct waitClientArgs {
sockaddr_in newSockAddr;
socklen_t newSockAddrSize;
int auxInt; // used in client mode because I didn't want to make another
// struct to keep track of
};
void *
waitForClient(void *argss)
{
waitClientArgs *args = static_cast<waitClientArgs *>(argss);
clientSocketDescriptor =
accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr, &args->newSockAddrSize);
if (clientSocketDescriptor >= 0) {
writeToFile(logFileName, "client connected");
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
delete args; // delete to avoid memory leaks
return pollForClient();
}
void *
pollForClient()
{
char msg[1024];
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);
if (msg[0] == '\0')
goto client_message_loop;
writeToFile(logFileName, msg);
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
return nullptr;
}
// set up a suitable server for this
int
setupServer(int port)
{
// setup a socket and connection tools
sockaddr_in servAddr;
bzero((char *)&servAddr, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(port);
// open stream oriented socket with internet address
// also keep track of the socket descriptor
serverSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocketDescriptor < 0) {
// keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error establishing the server socket!\n");
exit(0);
}
// bind the socket to its local address
int bindStatus =
bind(serverSocketDescriptor, (struct sockaddr *)&servAddr, sizeof(servAddr));
if (bindStatus < 0) {
// keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error binding socket to local address!\n");
exit(0);
}
writeToFile(logFileName, "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
// we need a new address to connect with the client
sockaddr_in newSockAddr;
socklen_t newSockAddrSize = sizeof(newSockAddr);
// accept, create a new socket descriptor to
// handle the new connection with client
auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->newSockAddr = newSockAddr;
aaa->newSockAddrSize = newSockAddrSize;
int rc = pthread_create(&client_wait_thread, nullptr, waitForClient, aaa);
pthread_detach(client_wait_thread);
writeToFile(logFileName, "Server started successfully");
gettimeofday(&start1, NULL);
return 0;
}
void
closeServer()
{
long e = end1.tv_sec - start1.tv_sec; /* the linter freaks out if you
don't save it as a variable */
// we need to close the socket descriptors after we're all done
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
close(serverSocketDescriptor);
/* Don't just die silently */
puts("********Session********");
printf("Bytes written: %i\n", bytesWritten);
printf("Bytes read: %i\n", bytesRead);
printf("Elapsed time: %ld secs\n", e);
puts("Connection closed.");
}
-9
View File
@@ -1,9 +0,0 @@
#ifndef SERVER_H
#define SERVER_H
void *waitForClient(void *argss);
void *pollForClient();
int setupServer(int port);
void closeServer();
#endif