29 Commits
Author SHA1 Message Date
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
16 changed files with 1049 additions and 22 deletions

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
old/client.txt
old/server.txt
old/main
+3 -10
View File
@@ -1,14 +1,7 @@
# threaded network chat refactor
https://www.youtube.com/watch?v=wecDU2t37gM
<iframe width="560" height="315" src="https://www.youtube.com/embed/wecDU2t37gM?si=YH4pZrsXo-9n_ryh" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
## 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.
The code has been refactored and it compiles. Hopefully it won't explode, but that if it does, that just means we have
actual meaningful work to do.
## TODO
@@ -23,7 +16,7 @@ and inconsistent. If you set up your editor correctly, it will take care of most
## Code
Everything is in [src/](src).
A mostly untouched copy of [Scott's original code](https://git.therats.win/scott/threaded_network_chat) is also in [old/](old).
A mostly untouched copy of [scott's original code](https://git.therats.win/scott/threaded_network_chat) is also in [old/](old).
A reformatted version of that code is also in [modified-example.cc](modified-example.cc) but it can be ignored.
## Build
+4 -3
View File
@@ -37,7 +37,7 @@ I recommend skimming over these for inspiration.
- return type goes on its own line,
- function name() is on a line below that, and
- 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
- **Try to make every line 100 characters wide or less.**
- Start source files as `.cc` straight away, even if they're pure **C**.
@@ -60,7 +60,7 @@ The length of a function name, variable name, et cetera should be directly propo
#### Don't
- abbreviate global variables **EVER**,
- abbreviate global variables,
- mention the data type in a variable name,
- use a full word where an abbreviation will do
- (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
- 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
## Source File layout
@@ -120,3 +120,4 @@ div_numb(int n, int d)
}
```
+5 -9
View File
@@ -86,9 +86,8 @@ displayFile(string path, int startLineNum = 0, int numLines = 10)
// 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
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);
@@ -133,8 +132,7 @@ void *
waitForClient(void *argss)
{
waitClientArgs *args = static_cast<waitClientArgs *>(argss);
clientSocketDescriptor =
accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr, &args->newSockAddrSize);
clientSocketDescriptor = accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr, &args->newSockAddrSize);
if (clientSocketDescriptor >= 0) {
writeToFile(logFileName, "client connected");
if (linesInFile(logFileName) > LOG_LENGTH)
@@ -186,8 +184,7 @@ setupServer(int port)
exit(0);
}
// bind the socket to its local address
int bindStatus =
bind(serverSocketDescriptor, (struct sockaddr *)&servAddr, sizeof(servAddr));
int bindStatus = bind(serverSocketDescriptor, (struct sockaddr *)&servAddr, sizeof(servAddr));
if (bindStatus < 0) {
// keeps from bricking the terminal if this happens
endwin();
@@ -291,8 +288,7 @@ setupClient()
sendSockAddr.sin_port = htons(PORT_NUM);
clientSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
// try to connect...
int status =
connect(clientSocketDescriptor, (sockaddr *)&sendSockAddr, sizeof(sendSockAddr));
int status = connect(clientSocketDescriptor, (sockaddr *)&sendSockAddr, sizeof(sendSockAddr));
if (status < 0)
writeToFile(logFileName, "Error connecting to socket!");
writeToFile(logFileName, "Connected to the server!");
+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;
}
+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 <fstream>
#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;
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);
}
+211
View File
@@ -0,0 +1,211 @@
#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...");
}