Compare commits

4 Commits

11 changed files with 616 additions and 3 deletions

19
.clang-format Normal file
View File

@@ -0,0 +1,19 @@
{
"BasedOnStyle": "LLVM",
"UseTab": "Always",
"IndentWidth": 8,
"TabWidth": 8,
"LineEnding": "LF",
"RemoveBracesLLVM": true,
"AlwaysBreakAfterReturnType": "AllDefinitions",
"BreakBeforeBraces": "Custom",
"BraceWrapping": {
"AfterFunction": true,
"AfterClass": false,
"AfterControlStatement": false,
"AfterNamespace": false,
"AfterStruct": false,
"BeforeElse": false,
"BeforeCatch": false
}
}

6
.gitignore vendored
View File

@@ -1,3 +1,3 @@
client.txt
server.txt
main
old/client.txt
old/server.txt
old/main

123
STYLE.md Normal file
View File

@@ -0,0 +1,123 @@
# Project Coding Style
**Subject to change, let's please discuss this.**
This is a less extreme version of the style I generally follow, and unless someone has a much better style I would
like to use this one for code we will be collaborating on. Feel free to ignore this if you plan for your own code to
be entirely your responsibility.
Also don't swear on anything we expect to present to the professor.
**Please install the [C/C++ Extension Pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools-extension-pack) and [clang-format](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format) for Visual Studio Code.**
Follow the instructions in the previous link to set up the correctly.
## TL;DR
Call files `.cc` instead of `.c` or `.cpp` and use `.h` headers for everything.
If you follow the setup instructions you should be fine. Otherwise:
Use [BSD Kernel Normal Form (KNF)](https://en.wikipedia.org/wiki/Indentation_style#BSD_KNF) for function bodies
and [Kernighan & Ritchie (K&R)](https://en.wikipedia.org/wiki/Indentation_style#K&R) style (with a few modifications stated in **General Guidelines**) everywhere else.
## Existing Literature
I recommend skimming over these for inspiration.
- [Names](https://research.swtch.com/names) - Russ Cox
- [Notes on Programming in C](http://doc.cat-v.org/bell_labs/pikestyle) - Rob Pike
## General Guidelines
- Use 8-width tabs, not spaces, for indentation.
- If you copy code from online, copy it by hand.
- Use [K&R style](https://en.wikipedia.org/wiki/Indentation_style#K&R) for indentation and brace placement where possible.
- When writing function bodies:
- return type goes on its own line,
- function name() is on a line below that, and
- open bracket `{` after that.
- `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 120 characters wide or less.**
- Start source files as `.cc` straight away, even if they're pure **C**.
### C++-specific guidelines
- file extension: `.cc`, `.h`
- C++ strings are fine
- Vectors are fine
- Use `printf` and co., not `std::cout`, unless somehow absolutely necessary
- `fstream`s for input/output/log files are fine
- Use C's `struct`s instead of objects unless you really really need an object.
- Use C-style type casting where possible
TL;DR pretend you're using C with basic modern conveniences. **When in doubt, ask.**
### Naming and Abbreviations
The length of a function name, variable name, et cetera should be directly proportional to its importance and lifetime.
#### Don't
- 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),
- use single-letter abbreviations for anything that lasts more than 15 lines, or
- abbreviate a word when an apt abbreviation does not exist.
#### 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++)`
- break any rule if following it ruins the readability
## Source File layout
```C++
/* C++ includes */
#include <iostream>
/* C (.h) includes */
#include <stdio.h>
/* local header includes */
#include "unicorns.h"
/* "using" directives */
using namespace std;
/* global constants */
const int life = 42;
/* global variables */
int degrees = 90;
/* function prototypes */
int div_numb(int n, int d);
/* main function body */
int
main(int argc, char *argv[])
{
int numerator = 32;
int denominator = 16;
int result;
puts("What is 32 divided by 16?");
result = div_numb(32, 16);
return 0;
}
/* other function bodies */
int
div_numb(int n, int d)
{
return n / d;
}
```

View File

@@ -0,0 +1,32 @@
root = true
[*]
charset = utf-8
indent_style = tab
indent_size = 8
tab_width = 8
end_of_line = lf
insert_final_newline = true
[*.{c,cc,cpp,h,hpp}]
# Unbraced single-statement blocks
cpp_remove_braces_for_single_line_control_statements = true
resharper_cpp_remove_braces_for_single_statements = true
resharper_cpp_add_braces_for_single_line_if = false
# Enforce BSD KNF for function bracess
cpp_new_line_before_open_brace_function = new_line
resharper_cpp_brace_style = next_line
resharper_cpp_function_definition_braces = next_line
# Enforce K&R for everything else
cpp_new_line_before_open_brace_namespace = same_line
cpp_new_line_before_open_brace_type = same_line
cpp_new_line_before_open_brace_block = same_line
cpp_new_line_before_open_brace_lambda = same_line
cpp_new_line_before_else = false
cpp_new_line_before_catch = false
resharper_cpp_namespace_declaration_braces = end_of_line
resharper_cpp_type_declaration_braces = end_of_line
resharper_cpp_control_transfer_braces = end_of_line

View File

@@ -0,0 +1,19 @@
{
"BasedOnStyle": "LLVM",
"UseTab": "Always",
"IndentWidth": 8,
"TabWidth": 8,
"LineEnding": "LF",
"RemoveBracesLLVM": true,
"AlwaysBreakAfterReturnType": "AllDefinitions",
"BreakBeforeBraces": "Custom",
"BraceWrapping": {
"AfterFunction": true,
"AfterClass": false,
"AfterControlStatement": false,
"AfterNamespace": false,
"AfterStruct": false,
"BeforeElse": false,
"BeforeCatch": false
}
}

View File

@@ -0,0 +1,20 @@
# BSD KNF funcs + K&R blocks + other stuff
# Requires clang-format >=16
BasedOnStyle: LLVM # defaults
UseTab: Always
IndentWidth: 8
TabWidth: 8
LineEnding: LF # unix
RemoveBracesLLVM: true # single-statement blocks
AlwaysBreakAfterReturnType: AllDefinitions # KNF functions
BreakBeforeBraces: Custom
BraceWrapping:
AfterFunction: true # KNF
AfterClass: false # K&R
AfterControlStatement: false # K&R
AfterNamespace: false # K&R
AfterStruct: false # K&R
BeforeElse: false # K&R
BeforeCatch: false # K&R

400
modified-example.cc Normal file
View File

@@ -0,0 +1,400 @@
/*an attempt at pthread and network and ncurses all in the same function
*/
#include <arpa/inet.h>
#include <curses.h>
#include <fstream>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <ostream>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector> //try to make the program work without this without doing anything awful but do it later
using namespace std;
const int PORT_NUM = 8888;
const string IP_ADDRESS = "127.0.0.1";
const int DEFAULT_CUR_X = 2; // the x position of the preferred default cursor
// position for message entry
const int DEFAULT_CUR_Y = 12; // the x position of the preferred default cursor
// position for message entry
int mode = 0; // what mode is this program in. 0 = nothing. 1 = server. 2 = client
int serverSocketDescriptor; // a global variable for storing the server socket
// descriptor.
int clientSocketDescriptor;
vector<int> clientSocketDescriptors; // a global vector for storing client
// socket descriptors
// keep track of the session time using global variables
struct timeval start1, end1;
// also keep track of the amount of data sent as well
int bytesRead, bytesWritten = 0;
const int LOG_LENGTH = 10; // number of text log file lines to display
int linePos = 0; // counter for what current position to use in the file
string logFileName; // the name of the log file
pthread_t client_wait_thread;
int writeToFile(string path, string line, bool incLineNum = true);
void *waitForClient(void *argss);
void *pollForClient();
void *pollForSever(void *args);
// clears ncurses rows in a specific region only
void
clearRows(int startingRow, int endingRow)
{
// preserve the cursor location
int yBefore, xBefore;
getyx(stdscr, yBefore, xBefore);
for (int i = startingRow; i < endingRow; i++) {
move(i, 0);
clrtoeol(); // clear to end of line
}
// restore original cursor location
move(yBefore, xBefore);
}
// display a file using ncurses
int
displayFile(string path, int startLineNum = 0, int numLines = 10)
{
ifstream file(path);
if (!file) {
return 1;
} else {
clearRows(0, numLines + 1); // clear the chat area
int lineNum = 0;
string line;
// print each line directly to the screen
int num = 0;
while (getline(file, line) &&
num <= numLines + startLineNum +
1) // while there is file content and the line
// number isn't too high
{
if (num >= startLineNum) {
move(lineNum, 0);
printw("%s", line.c_str());
lineNum++; // increment the row number after
// printing each line
}
num++;
}
move(DEFAULT_CUR_Y, DEFAULT_CUR_X);
refresh();
return 0;
}
}
// gets the number of lines in a file. returns -1 if there was an error.
int
linesInFile(string path)
{
ifstream file(path);
if (!file) {
return 1;
} else {
int lineNum = 0;
string line;
int num = 0;
while (getline(file, line))
num++;
return num;
}
}
struct waitClientArgs {
sockaddr_in newSockAddr;
socklen_t newSockAddrSize;
int auxInt; // used in client mode because I didn't want to make another
// struct to keep track of
};
void *
waitForClient(void *argss)
{
waitClientArgs *args = static_cast<waitClientArgs *>(argss);
clientSocketDescriptor =
accept(serverSocketDescriptor, (sockaddr *)&args->newSockAddr,
&args->newSockAddrSize);
if (clientSocketDescriptor >= 0) {
writeToFile(logFileName, "client connected");
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
delete args; // delete to avoid memory leaks
return pollForClient();
}
void *
pollForClient()
{
char msg[1024];
while (1) {
// receive a message from the client (listen)
memset(&msg, 0, sizeof(msg)); // clear the buffer
bytesRead +=
recv(clientSocketDescriptor, (char *)&msg, sizeof(msg), 0);
writeToFile(logFileName,
msg); // write the client's message to file
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
return nullptr;
}
// set up a suitable server for this
int
setupServer(int port)
{
// setup a socket and connection tools
sockaddr_in servAddr;
bzero((char *)&servAddr, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(port);
// open stream oriented socket with internet address
// also keep track of the socket descriptor
serverSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocketDescriptor < 0) {
// keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error establishing the server socket!\n");
exit(0);
}
// bind the socket to its local address
int bindStatus = bind(serverSocketDescriptor,
(struct sockaddr *)&servAddr, sizeof(servAddr));
if (bindStatus < 0) {
// keeps from bricking the terminal if this happens
endwin();
fprintf(stderr, "Error binding socket to local address!\n");
exit(0);
}
writeToFile(logFileName, "Waiting for a client to connect...");
// listen for up to 5 requests at a time
listen(serverSocketDescriptor, 5);
// receive a request from client using accept
// we need a new address to connect with the client
sockaddr_in newSockAddr;
socklen_t newSockAddrSize = sizeof(newSockAddr);
// accept, create a new socket descriptor to
// handle the new connection with client
auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->newSockAddr = newSockAddr;
aaa->newSockAddrSize = newSockAddrSize;
int rc =
pthread_create(&client_wait_thread, nullptr, waitForClient, aaa);
pthread_detach(client_wait_thread);
writeToFile(logFileName, "Server started sucessfully");
gettimeofday(&start1, NULL);
return 0;
}
void
closeServer()
{
long e = end1.tv_sec - start1.tv_sec; /* the linter freaks out if you
don't save it as a variable */
// we need to close the socket descriptors after we're all done
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
close(serverSocketDescriptor);
/* Don't just die silently */
printf("********Session********\n");
printf("Bytes written: %i\nBytes Read: %i\n", bytesWritten, bytesRead);
printf("Elapsed time: %ld\n secs\n", e);
printf("Connection closed...\n");
}
void
closeClient()
{
long e = end1.tv_sec - start1.tv_sec;
gettimeofday(&end1, NULL);
close(clientSocketDescriptor);
printf("********Session********");
printf("Bytes written: %i\nBytes read: %i\n", bytesWritten, bytesRead);
printf("Elapsed time: %ld\n secs\n", e);
printf("Connection closed...\n");
}
void *
pollForSever(void *args)
{
waitClientArgs *aaa = static_cast<waitClientArgs *>(args);
int socketDescriptor = (int)aaa->auxInt;
char msg[1024];
while (1) {
memset(&msg, 0, sizeof(msg)); // clear the buffer
bytesRead +=
recv(socketDescriptor, (char *)&msg, sizeof(msg), 0);
// not needed for proofs of concept testing
/*if(!strcmp(msg, "exit"))
{
writeToFile(logFileName, msg);
displayFile(logFileName, linePos, LOG_LENGTH);
break;
}*/
// cout << "Server: " << msg << endl;
// printf("Server: %s\n");
writeToFile(logFileName, msg);
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
displayFile(logFileName, linePos, LOG_LENGTH);
}
}
void
setupClient()
{
// we need 2 things: ip address and port number, in that order
// if(argc != 3)
//{
// cerr << "Usage: ip_address port" << endl; exit(0);
// } //grab the IP address and port number
// create a message buffer
char msg[1024];
// setup a socket and connection tools
struct hostent *host = gethostbyname(IP_ADDRESS.c_str());
sockaddr_in sendSockAddr;
bzero((char *)&sendSockAddr, sizeof(sendSockAddr));
sendSockAddr.sin_family = AF_INET;
sendSockAddr.sin_addr.s_addr =
inet_addr(inet_ntoa(*(struct in_addr *)*host->h_addr_list));
sendSockAddr.sin_port = htons(PORT_NUM);
clientSocketDescriptor = socket(AF_INET, SOCK_STREAM, 0);
// try to connect...
int status = connect(clientSocketDescriptor, (sockaddr *)&sendSockAddr,
sizeof(sendSockAddr));
if (status < 0)
writeToFile(logFileName, "Error connecting to socket!");
writeToFile(logFileName, "Connected to the server!");
int bytesRead, bytesWritten = 0;
struct timeval start1, end1;
gettimeofday(&start1, NULL);
auto *aaa = new waitClientArgs{}; // it looks stupid but it works
aaa->auxInt = clientSocketDescriptor;
int rc =
pthread_create(&client_wait_thread, nullptr, pollForSever, aaa);
pthread_detach(client_wait_thread);
}
// appends a line of text to the end of a given file. returns 0 if the file
// existed, 1 if it didn't work
int
writeToFile(string path, string line, bool incLineNum)
{
ofstream file;
file.open(path, ios_base::app); // open the file in append mode
if (file.is_open()) {
file << line << endl;
// this probably would help but theres too much broken stuff
// right now to be sure if (incLineNum)
//{
// linePos++;
// }
return 0;
} else {
// do something if it didn't work
return 1; // i guess that's good enough for now
}
}
int
main(int argc, char *argv[])
{
if (argc > 1 && argv[1][0] == 'c') {
// client mode
logFileName = "client.txt";
mode = 2;
} else {
// server mode
logFileName = "server.txt";
mode = 1;
}
// putting this lower down to circumvent the terminal brick when the
// socket was already in use results in all network functonality no
// longer working.
initscr(); // creates stdscr. important step
use_default_colors(); // this apparently tells it to use default
// terminal colors whenever there is no color
// attribute applied
// printw("Starting server...");
// you have to do this to make the scrolling still work correctly if
// there was already content in the log file
linePos = linesInFile(logFileName) - LOG_LENGTH + 1;
char *userInput = new char[1024];
bool exit = false;
if (mode == 2) {
writeToFile(logFileName, "CLIENT MODE");
setupClient();
} else if (mode == 1) {
writeToFile(logFileName, "SERVER MODE");
setupServer(PORT_NUM);
}
while (!exit) {
displayFile(logFileName, linePos, LOG_LENGTH);
// scroll along the screen if and when required so that it stays
// in sync
if (linesInFile(logFileName) > LOG_LENGTH)
linePos++;
move(12, 0);
printw("> ");
getstr(userInput);
writeToFile(logFileName, userInput);
if (mode == 1) {
send(clientSocketDescriptor, (char *)userInput,
strlen(userInput), 0);
} else {
send(clientSocketDescriptor, (char *)userInput,
strlen(userInput), 0);
}
}
endwin();
closeServer();
return 0;
}