Compare commits

29 Commits

Author SHA1 Message Date
ed437d9b05 ugh 2 2026-03-09 13:26:51 -05:00
2c61dbcc64 ugh 2026-03-09 13:26:08 -05:00
442bc8d88f EVEN FURTHER BIKESHEDDING 2026-03-09 13:23:13 -05:00
081a278753 even further bikeshedding 2026-03-09 13:20:40 -05:00
cae85a7466 remove unnecessary include 2026-03-09 13:15:39 -05:00
0950342de0 ok now im done 2026-03-09 13:08:09 -05:00
3f899a974f very formatting stuff 2026-03-09 13:05:32 -05:00
039e9abef5 update modified-example.cc to match new column limit 2026-03-09 13:00:46 -05:00
6bc287d6cc update README.md 2026-03-09 12:59:55 -05:00
37d8b7fd0f delete empty file net.h 2026-03-09 12:57:43 -05:00
e68d98a669 update files to match new max column width 100 2026-03-09 12:57:24 -05:00
ce5d66f712 update ColumnLimit 2026-03-09 12:55:21 -05:00
a9ed43b230 update STYLE.md 2026-03-09 12:54:36 -05:00
e8d2e015b3 update README.md 2026-03-09 12:54:07 -05:00
bd2debcf7d write big ugly panic message for when main() freaks out too much 2026-03-09 12:45:24 -05:00
59e34977da oops 2026-03-09 12:32:22 -05:00
0f66b7f4c2 refactor: move display-related functions to display.cc 2026-03-09 12:32:02 -05:00
08b7809c34 refactor: move file operations into file.cc 2026-03-09 12:26:34 -05:00
a1ff9dd62d oops 2026-03-09 12:23:21 -05:00
a2a5f6eb57 refactor: move client/server funcs to net.cc 2026-03-09 12:22:40 -05:00
fa77abd446 copy .clang-format to src/ 2026-03-08 19:39:48 -05:00
da4dfbdd3e move source to src/ 2026-03-08 19:39:02 -05:00
224e59549c oops 2026-03-08 13:37:24 -05:00
5b8dcbdc1a add readme 2026-03-08 13:36:48 -05:00
e5bb323a2f ugh 2026-03-08 13:34:11 -05:00
1368695e74 refactor done? maybe? 2026-03-08 13:11:52 -05:00
53dd322284 refactor pt 2 2026-03-08 12:52:18 -05:00
09fcc17caa refactor wip 2026-03-08 01:29:25 -06:00
54ba2d8067 update ColumnLimit to match STYLE.md 2026-03-07 16:49:21 -06:00
14 changed files with 587 additions and 33 deletions

View File

@@ -3,6 +3,7 @@
"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",

33
README.md Normal file
View File

@@ -0,0 +1,33 @@
# threaded network chat refactor
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?
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
## 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 reformatted version of that code is also in [modified-example.cc](modified-example.cc) but it can be ignored.
## Build
```
cd src/
make
```

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.
- `for`, `if`, `while`, etc. statements that only contain one line - no brackets on `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 120 characters wide or less.** - **Try to make every line 100 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, - abbreviate global variables **EVER**,
- 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,4 +120,3 @@ div_numb(int n, int d)
} }
``` ```

View File

@@ -3,6 +3,7 @@
"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",

View File

@@ -4,6 +4,7 @@ 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

View File

@@ -30,7 +30,7 @@ const int DEFAULT_CUR_X = 2; // the x position of the preferred default cursor
const int DEFAULT_CUR_Y = 12; // the x position of the preferred default cursor const int DEFAULT_CUR_Y = 12; // the x position of the preferred default cursor
// position for message entry // position for message entry
int mode = 0; // what mode is this program in. 0 = nothing. 1 = server. 2 = client 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 int serverSocketDescriptor; // a global variable for storing the server socket
// descriptor. // descriptor.
int clientSocketDescriptor; int clientSocketDescriptor;
@@ -44,7 +44,7 @@ struct timeval start1, end1;
int bytesRead, bytesWritten = 0; int bytesRead, bytesWritten = 0;
const int LOG_LENGTH = 10; // number of text log file lines to display 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 int linePos = 0; // counter for what current position to use in the file
string logFileName; // the name of the log file string logFileName; // the name of the log file
@@ -87,9 +87,8 @@ displayFile(string path, int startLineNum = 0, int numLines = 10)
// print each line directly to the screen // print each line directly to the screen
int num = 0; int num = 0;
while (getline(file, line) && while (getline(file, line) &&
num <= numLines + startLineNum + num <= numLines + startLineNum + 1) // while there is file content and the
1) // while there is file content and the line // line number isn't too high
// number isn't too high
{ {
if (num >= startLineNum) { if (num >= startLineNum) {
move(lineNum, 0); move(lineNum, 0);
@@ -135,8 +134,7 @@ waitForClient(void *argss)
{ {
waitClientArgs *args = static_cast<waitClientArgs *>(argss); waitClientArgs *args = static_cast<waitClientArgs *>(argss);
clientSocketDescriptor = clientSocketDescriptor =
accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr, accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr, &args->newSockAddrSize);
&args->newSockAddrSize);
if (clientSocketDescriptor >= 0) { if (clientSocketDescriptor >= 0) {
writeToFile(logFileName, "client connected"); writeToFile(logFileName, "client connected");
if (linesInFile(logFileName) > LOG_LENGTH) if (linesInFile(logFileName) > LOG_LENGTH)
@@ -155,8 +153,7 @@ pollForClient()
while (1) { while (1) {
// receive a message from the client (listen) // receive a message from the client (listen)
memset(&msg, 0, sizeof(msg)); // clear the buffer memset(&msg, 0, sizeof(msg)); // clear the buffer
bytesRead += bytesRead += recv(clientSocketDescriptor, (char *)&msg, sizeof(msg), 0);
recv(clientSocketDescriptor, (char *)&msg, sizeof(msg), 0);
writeToFile(logFileName, writeToFile(logFileName,
msg); // write the client's message to file msg); // write the client's message to file
if (linesInFile(logFileName) > LOG_LENGTH) if (linesInFile(logFileName) > LOG_LENGTH)
@@ -189,8 +186,8 @@ setupServer(int port)
exit(0); exit(0);
} }
// bind the socket to its local address // bind the socket to its local address
int bindStatus = bind(serverSocketDescriptor, int bindStatus =
(struct sockaddr *)&servAddr, sizeof(servAddr)); bind(serverSocketDescriptor, (struct sockaddr *)&servAddr, sizeof(servAddr));
if (bindStatus < 0) { if (bindStatus < 0) {
// keeps from bricking the terminal if this happens // keeps from bricking the terminal if this happens
endwin(); endwin();
@@ -210,8 +207,7 @@ setupServer(int port)
auto *aaa = new waitClientArgs{}; // it looks stupid but it works auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->newSockAddr = newSockAddr; aaa->newSockAddr = newSockAddr;
aaa->newSockAddrSize = newSockAddrSize; aaa->newSockAddrSize = newSockAddrSize;
int rc = int rc = pthread_create(&client_wait_thread, nullptr, waitForClient, aaa);
pthread_create(&client_wait_thread, nullptr, waitForClient, aaa);
pthread_detach(client_wait_thread); pthread_detach(client_wait_thread);
writeToFile(logFileName, "Server started sucessfully"); writeToFile(logFileName, "Server started sucessfully");
gettimeofday(&start1, NULL); gettimeofday(&start1, NULL);
@@ -258,8 +254,7 @@ pollForSever(void *args)
char msg[1024]; char msg[1024];
while (1) { while (1) {
memset(&msg, 0, sizeof(msg)); // clear the buffer memset(&msg, 0, sizeof(msg)); // clear the buffer
bytesRead += bytesRead += recv(socketDescriptor, (char *)&msg, sizeof(msg), 0);
recv(socketDescriptor, (char *)&msg, sizeof(msg), 0);
// not needed for proofs of concept testing // not needed for proofs of concept testing
/*if(!strcmp(msg, "exit")) /*if(!strcmp(msg, "exit"))
@@ -292,13 +287,12 @@ setupClient()
sockaddr_in sendSockAddr; sockaddr_in sendSockAddr;
bzero((char *)&sendSockAddr, sizeof(sendSockAddr)); bzero((char *)&sendSockAddr, sizeof(sendSockAddr));
sendSockAddr.sin_family = AF_INET; sendSockAddr.sin_family = AF_INET;
sendSockAddr.sin_addr.s_addr = sendSockAddr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)*host->h_addr_list));
inet_addr(inet_ntoa(*(struct in_addr *)*host->h_addr_list));
sendSockAddr.sin_port = htons(PORT_NUM); sendSockAddr.sin_port = htons(PORT_NUM);
clientSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0); clientSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
// try to connect... // try to connect...
int status = connect(clientSocketDescriptor, (sockaddr *)&sendSockAddr, int status =
sizeof(sendSockAddr)); connect(clientSocketDescriptor, (sockaddr *)&sendSockAddr, sizeof(sendSockAddr));
if (status < 0) if (status < 0)
writeToFile(logFileName, "Error connecting to socket!"); writeToFile(logFileName, "Error connecting to socket!");
writeToFile(logFileName, "Connected to the server!"); writeToFile(logFileName, "Connected to the server!");
@@ -307,8 +301,7 @@ setupClient()
gettimeofday(&start1, NULL); gettimeofday(&start1, NULL);
auto *aaa = new waitClientArgs{}; // it looks stupid but it works auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->auxInt = clientSocketDescriptor; aaa->auxInt = clientSocketDescriptor;
int rc = int rc = pthread_create(&client_wait_thread, nullptr, pollForSever, aaa);
pthread_create(&client_wait_thread, nullptr, pollForSever, aaa);
pthread_detach(client_wait_thread); pthread_detach(client_wait_thread);
} }
@@ -384,13 +377,10 @@ main(int argc, char *argv[])
printw("> "); printw("> ");
getstr(userInput); getstr(userInput);
writeToFile(logFileName, userInput); writeToFile(logFileName, userInput);
if (mode == 1) { if (mode == 1)
send(clientSocketDescriptor, (char *)userInput, send(clientSocketDescriptor, (char *)userInput, strlen(userInput), 0);
strlen(userInput), 0); else
} else { send(clientSocketDescriptor, (char *)userInput, strlen(userInput), 0);
send(clientSocketDescriptor, (char *)userInput,
strlen(userInput), 0);
}
} }
endwin(); endwin();

20
src/.clang-format Normal file
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
src/Makefile Normal file
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
src/README.md Normal file
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
src/chat.h Normal file
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
src/display.cc Normal file
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
src/file.cc Normal file
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
src/main.cc Normal file
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
src/net.cc Normal file
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...");
}