38 Commits
Author SHA1 Message Date
jazz ed437d9b05 ugh 2 2026-03-09 13:26:51 -05:00
jazz 2c61dbcc64 ugh 2026-03-09 13:26:08 -05:00
jazz 442bc8d88f EVEN FURTHER BIKESHEDDING 2026-03-09 13:23:13 -05:00
jazz 081a278753 even further bikeshedding 2026-03-09 13:20:40 -05:00
jazz cae85a7466 remove unnecessary include 2026-03-09 13:15:39 -05:00
jazz 0950342de0 ok now im done 2026-03-09 13:08:09 -05:00
jazz 3f899a974f very formatting stuff 2026-03-09 13:05:32 -05:00
jazz 039e9abef5 update modified-example.cc to match new column limit 2026-03-09 13:00:46 -05:00
jazz 6bc287d6cc update README.md 2026-03-09 12:59:55 -05:00
jazz 37d8b7fd0f delete empty file net.h 2026-03-09 12:57:43 -05:00
jazz e68d98a669 update files to match new max column width 100 2026-03-09 12:57:24 -05:00
jazz ce5d66f712 update ColumnLimit 2026-03-09 12:55:21 -05:00
jazz a9ed43b230 update STYLE.md 2026-03-09 12:54:36 -05:00
jazz e8d2e015b3 update README.md 2026-03-09 12:54:07 -05:00
jazz bd2debcf7d write big ugly panic message for when main() freaks out too much 2026-03-09 12:45:24 -05:00
jazz 59e34977da oops 2026-03-09 12:32:22 -05:00
jazz 0f66b7f4c2 refactor: move display-related functions to display.cc 2026-03-09 12:32:02 -05:00
jazz 08b7809c34 refactor: move file operations into file.cc 2026-03-09 12:26:34 -05:00
jazz a1ff9dd62d oops 2026-03-09 12:23:21 -05:00
jazz a2a5f6eb57 refactor: move client/server funcs to net.cc 2026-03-09 12:22:40 -05:00
jazz fa77abd446 copy .clang-format to src/ 2026-03-08 19:39:48 -05:00
jazz da4dfbdd3e move source to src/ 2026-03-08 19:39:02 -05:00
jazz 224e59549c oops 2026-03-08 13:37:24 -05:00
jazz 5b8dcbdc1a add readme 2026-03-08 13:36:48 -05:00
jazz e5bb323a2f ugh 2026-03-08 13:34:11 -05:00
jazz 1368695e74 refactor done? maybe? 2026-03-08 13:11:52 -05:00
jazz 53dd322284 refactor pt 2 2026-03-08 12:52:18 -05:00
jazz 09fcc17caa refactor wip 2026-03-08 01:29:25 -06:00
jazz 54ba2d8067 update ColumnLimit to match STYLE.md 2026-03-07 16:49:21 -06:00
jazz 5ca28bf334 add example from scott's initial version 2026-03-07 16:38:48 -06:00
jazz d926b81265 add style guidelines STYLE.md 2026-03-07 16:38:19 -06:00
jazz 143ec79d37 add editorconfig and clang-format files 2026-03-07 16:37:55 -06:00
jazz 7a0e2de855 refactor: initial commit 2026-03-07 16:26:33 -06:00
jazz 2ae315d33a remove unnecessary fcntl.h, add ostream, replace cout/cerror with print statements, remove outdated comments 2026-03-04 15:19:42 -06:00
jazz dcaa577834 add gitignore 2026-03-04 15:18:24 -06:00
Your Name cf41cb45e1 updated readme to describe how to circumvent the bug 2026-03-01 18:52:47 -06:00
Your Name 795f90e865 got it to work a lot better 2026-02-28 11:02:21 -06:00
Your Name 6f24ef2fb7 initial commit 2026-02-26 11:16:17 -06:00
29 changed files with 1430 additions and 583 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
old/client.txt
old/server.txt
old/main
-18
View File
@@ -1,18 +0,0 @@
# Compiler and flags
TARGET = ct
CXX = g++
CXXFLAGS = -pthread -Wall -Wextra -O2 -std=c++17 -lcurses -g
SRC = $(wildcard *.cc)
OBJ = $(SRC:.cc=.o)
all: $(TARGET)
clean:
rm -f $(TARGET) *.o
$(TARGET): $(OBJ)
$(CXX) $(CXXFLAGS) $(OBJ) -o $(TARGET)
%.o: %.cc
$(CXX) $(CXXFLAGS) -c $< -o $@
+2 -4
View File
@@ -1,9 +1,7 @@
# threaded network chat refactor
The functions have been split into semantically appropriate files but almost nothing whatsoever
has been modified.
It APPEARS that this version actually works. I tested it a little.
The code has been refactored and it compiles. Hopefully it won't explode, but we were going to be
doing a bunch of bug fixes anyway.
## What now?
-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.
File renamed without changes.
File renamed without changes.
-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;
}
+390
View File
@@ -0,0 +1,390 @@
/*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
+20
View File
@@ -0,0 +1,20 @@
{
"BasedOnStyle": "LLVM",
"UseTab": "Always",
"IndentWidth": 8,
"TabWidth": 8,
"ColumnLimit": 100,
"LineEnding": "LF",
"RemoveBracesLLVM": true,
"AlwaysBreakAfterReturnType": "AllDefinitions",
"BreakBeforeBraces": "Custom",
"BraceWrapping": {
"AfterFunction": true,
"AfterClass": false,
"AfterControlStatement": false,
"AfterNamespace": false,
"AfterStruct": false,
"BeforeElse": false,
"BeforeCatch": false
}
}
+20
View File
@@ -0,0 +1,20 @@
CXX = g++
CXXFLAGS = -pthread -Wall -Wextra -O2 -std=c++17
LDFLAGS = -lcurses
TARGET = ct
CXX_SRCS = main.cc net.cc file.cc display.cc
CXX_OBJS = $(CXX_SRCS:.cc=.o)
all: $(TARGET)
$(TARGET): $(CXX_OBJS)
$(CXX) $(CXXFLAGS) $(LDFLAGS) $(CXX_OBJS) -o $(TARGET) $(LDFLAGS)
%.o: %.cc
$(CXX) $(CXXFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -f *.o $(TARGET)
+6
View File
@@ -0,0 +1,6 @@
# refactor v1
I'm trying to split this up into multiple pieces but that probably involves using fewer global variables and passing the
same data between the functions that use them.
global variables will use `snake_case` and global constants will use `SCREAMING_SNAKE_CASE`.
+30
View File
@@ -0,0 +1,30 @@
#ifndef CHAT_H
#define CHAT_H
#include <string>
#include <netinet/in.h>
#include <pthread.h>
#define CURSOR_DEFAULT_POSITION_X 2
#define CURSOR_DEFAULT_POSITION_Y 12
#define LOG_LENGTH 10
typedef struct __wclient_args_t wclient_args_t;
void *await_client(void *a);
void big_panic_msg(void);
void clear_rows(int s, int e);
int count_lines(std::string p);
int file_disp(std::string p, int lnstart, int lncount = 10);
int log_append(std::string p, std::string ln);
int log_linect(std::string p);
void client_start(std::string ip, int port, int *sock, std::string log, pthread_t *thr);
void client_stop(struct timeval *start, struct timeval *end, int *sock, int *bread, int *bwrit);
void *poll_client(int *sock, int *bread, std::string log, int *lpos);
void *poll_server(void *args);
int
server_start(int port, int *sock, std::string log, pthread_t *thr, struct timeval *ts);
void server_stop(struct timeval *start, struct timeval *end, int *sock, int *bread, int *bwrit);
#endif
+50
View File
@@ -0,0 +1,50 @@
#include <fstream>
#include <curses.h>
#include "chat.h"
void
clear_rows(int s, int e)
{
/* preserve cursor location */
int x_bef;
int y_bef;
getyx(stdscr, y_bef, x_bef);
for (int i = s; i < e; i++) {
move(i, 0);
clrtoeol();
}
move(y_bef, x_bef);
}
int
file_disp(std::string p, int lnstart, int lncount)
{
std::ifstream f(p);
if (!f)
return 1;
int i = 0;
int ln_no = 0;
std::string ln;
clear_rows(0, lncount + 1); /* clear chat window */
while (getline(f, ln) && i <= ln_no + lnstart + 1) {
if (i >= lnstart) {
move(ln_no, 0);
printw("%s", ln.c_str());
ln_no++;
}
i++;
}
move(CURSOR_DEFAULT_POSITION_Y, CURSOR_DEFAULT_POSITION_X);
refresh();
return 0;
}
+41
View File
@@ -0,0 +1,41 @@
#include <algorithm>
#include <fstream>
#include <iterator>
#include "chat.h"
int
count_lines(std::string p)
{
int c = 0;
std::ifstream f(p);
if (!f)
return c;
/* Apparently this is the most efficient way to count all lines in a file. How revolting! */
c = std::count(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>(), '\n');
return c;
}
int
log_append(std::string p, std::string ln)
{
std::ofstream f;
f.open(p, std::ios_base::app);
if (!f.is_open())
return 1;
f << ln << std::endl;
f.close();
// "this probably would help but theres too much broken stuff right now to be sure" - scott
// bool inclnum should be added to function if we do decide to use this block:
// if (inclnum)
// line_position++;
return 0;
}
+150
View File
@@ -0,0 +1,150 @@
#include <string>
#include <arpa/inet.h>
#include <bits/types/struct_timeval.h>
#include <curses.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include "chat.h"
typedef enum { NO_MODE = 0, SERVER_MODE = 1, CLIENT_MODE = 2 } chatmode_t;
chatmode_t mode = NO_MODE;
int line_position = 0;
struct timeval time_start;
struct timeval time_end;
int bytes_read;
int bytes_written;
int listen_port = 9999;
std::string listen_ip_address = "127.0.0.1";
std::string log_path;
int socket_descriptor_server;
int socket_descriptor_client;
/* We use this for both. Probably won't be like this forever. */
pthread_t client_wait_thread;
int
main(int argc, char *argv[])
{
int err = 0;
char *usr_in = new char[1024];
bool quit = false;
if (argc > 1 && (!strcmp(argv[1], "client")))
mode = CLIENT_MODE;
else
mode = SERVER_MODE;
initscr();
use_default_colors();
line_position = count_lines(log_path) - LOG_LENGTH + 1;
switch (mode) {
case CLIENT_MODE:
log_append(log_path, "CLIENT MODE");
client_start(listen_ip_address, listen_port, &socket_descriptor_client, log_path,
&client_wait_thread);
break;
case SERVER_MODE:
log_append(log_path, "SERVER MODE");
err = server_start(listen_port, &socket_descriptor_server, log_path,
&client_wait_thread, &time_start);
if (err)
goto panic_no_server_sock;
break;
default:
goto panic_no_mode;
break;
}
while (!quit) {
file_disp(log_path, line_position, LOG_LENGTH);
if (count_lines(log_path) > LOG_LENGTH)
line_position++;
/* prompt for input */
move(CURSOR_DEFAULT_POSITION_Y, 0);
printw("> ");
/* await input */
getstr(usr_in);
log_append(log_path, usr_in);
send(socket_descriptor_client, (char *)usr_in, strlen(usr_in), 0);
}
/* Final exit point if everything goes OK */
endwin();
switch (mode) {
case CLIENT_MODE:
client_stop(&time_start, &time_end, &socket_descriptor_client, &bytes_read,
&bytes_written);
break;
case SERVER_MODE:
server_stop(&time_start, &time_end, &socket_descriptor_server, &bytes_read,
&bytes_written);
break;
default:
goto panic_no_mode;
break;
}
return 0;
panic_no_mode:
big_panic_msg();
if (mode == NO_MODE)
fprintf(stderr, "Fatal: Mode was not set!\n");
else
fprintf(stderr, "Fatal: Mode was set to invalid enum %i\n", mode);
return 1;
panic_no_server_sock:
big_panic_msg();
fprintf(stderr, "Fatal: Could not establish server socket!\n");
return 1;
}
/*
* For when things go really really bad.
* Check if there is a window and if so, close it.
* Spit out a really loud obnoxious error afterwards.
*/
void
big_panic_msg(void)
{
if (!isendwin())
endwin();
fprintf(stderr, "-----FATAL ERROR, THIS SHOULD NEVER HAPPEN-----\n");
usleep(400 * 1000);
fprintf(stderr, "You may ask yourself... how do I work this?\n");
putchar(7);
usleep(400 * 1000);
fprintf(stderr, "And you may find yourself... past the point of return 0;!\n");
putchar(7);
usleep(400 * 1000);
fprintf(stderr, "And you may ask yourself... well, how did I get here?\n");
putchar(7);
}
+212
View File
@@ -0,0 +1,212 @@
#include <string>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <unistd.h>
#include "chat.h"
typedef struct __wclient_args_t {
sockaddr_in new_addr;
socklen_t new_addr_siz;
int aux;
int *bread;
int *sock;
std::string log;
int *lpos;
} wclient_args_t;
typedef struct __wserver_args_t {
sockaddr_in new_addr;
socklen_t new_addr_siz;
int aux;
int *bread;
int *lpos;
std::string log;
} wserver_args_t;
/*
* client_start(listen_ip, listen_port, &socket_descriptor_client, log_path, &client_wait_thread);
*/
void
client_start(std::string ip, int port, int *sock, std::string log, pthread_t *thr)
{
char msg[1024];
int bread;
int bwrit;
sockaddr_in send_addr;
struct timeval time_start;
struct timeval time_end;
struct hostent *host = gethostbyname(ip.c_str());
auto *a = new wclient_args_t{};
bzero((char *)&send_addr, sizeof(send_addr));
send_addr.sin_family = AF_INET;
send_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)*host->h_addr_list));
send_addr.sin_port = htons(port);
*sock = socket(AF_INET, SOCK_STREAM, 0);
attempt_client_connection:
int stat = connect(*sock, (sockaddr *)&send_addr, sizeof(send_addr));
if (stat < 0) {
log_append(log, "Error connecting to socket, retrying...");
goto attempt_client_connection;
}
log_append(log, "Connected to the server!");
bread = bwrit = 0;
gettimeofday(&time_start, NULL);
a->aux = *sock;
pthread_create(thr, nullptr, poll_server, a);
pthread_detach(*thr);
}
int
server_start(int port, int *sock, std::string log, pthread_t *thr, struct timeval *ts)
{
auto *a = new wclient_args_t{};
sockaddr_in srv_addr;
sockaddr_in newsock;
socklen_t newsock_siz = sizeof(newsock);
int bstat;
int rc;
/* set up socket and connection tools */
bzero((char *)&srv_addr, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
srv_addr.sin_port = htons(port);
/* open stream-oriented socket w inet addr and track sock descriptor */
*sock = socket(AF_INET, SOCK_STREAM, 0);
if (*sock < 0)
return 1;
/* bind sock to its local addr */
bstat = bind(*sock, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
if (bstat < 0)
return 1;
log_append(log, "Waiting for a client to connect...");
/* listen for up to 5 simultaneous requests */
listen(*sock, 5);
a->new_addr = newsock;
a->new_addr_siz = newsock_siz;
rc = pthread_create(thr, nullptr, await_client, a);
pthread_detach(*thr);
log_append(log, "Server started successfully.");
gettimeofday(ts, NULL);
return 0;
}
void *
await_client(void *args)
{
wclient_args_t *a = static_cast<wclient_args_t *>(a);
*a->sock = accept(*a->sock, (sockaddr *)&a->new_addr, &a->new_addr_siz);
if (*a->sock >= 0) {
log_append(a->log, "client connected");
if (count_lines(a->log) > LOG_LENGTH)
++*a->lpos;
file_disp(a->log, *a->lpos, LOG_LENGTH);
}
/* get what we need for the return and throw the rest away */
int *s = a->sock;
int *b = a->bread;
std::string l = a->log;
int *p = a->lpos;
delete a;
return poll_client(s, b, l, p);
}
void *
poll_client(int *sock, int *bread, std::string log, int *lpos)
{
char msg[1024];
for (;;) {
/* listen for msg from client */
memset(&msg, 0, sizeof(msg)); /* clear buf */
*bread += recv(*sock, (char *)&msg, sizeof(msg), 0);
log_append(log, msg);
if (count_lines(log) > LOG_LENGTH)
++*lpos;
file_disp(log, *lpos, LOG_LENGTH);
}
return nullptr;
}
void *
poll_server(void *args)
{
wserver_args_t *a = static_cast<wserver_args_t *>(args);
int sock_desc = (int)a->aux;
char msg[1024];
for (;;) {
memset(&msg, 0, sizeof(msg));
*a->bread += recv(sock_desc, (char *)&msg, sizeof(msg), 0);
log_append(a->log, msg);
if (count_lines(a->log) > LOG_LENGTH)
++*a->lpos;
file_disp(a->log, *a->lpos, LOG_LENGTH);
}
delete a;
}
void
server_stop(struct timeval *start, struct timeval *end, int *sock, int *bread, int *bwrit)
{
long e = end->tv_sec - start->tv_sec;
gettimeofday(end, NULL);
close(*sock);
/* Don't just die silently */
puts("********Session********");
printf("Bytes written: %i\nBytes Read: %i\n", *bwrit, *bread);
printf("Elapsed time: %ld\n secs\n", e);
puts("Connection closed...");
}
void
client_stop(struct timeval *start, struct timeval *end, int *sock, int *bread, int *bwrit)
{
long e = end->tv_sec - start->tv_sec;
gettimeofday(end, NULL);
close(*sock);
puts("********Session********");
printf("Bytes written: %i\nBytes read: %i\n", *bwrit, *bread);
printf("Elapsed time: %ld\n secs\n", e);
puts("Connection closed...");
}