diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c8a42a4 --- /dev/null +++ b/.clang-format @@ -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 + } +} diff --git a/modified-example.cc b/modified-example.cc new file mode 100644 index 0000000..f745311 --- /dev/null +++ b/modified-example.cc @@ -0,0 +1,400 @@ +/*an attempt at pthread and network and ncurses all in the same function + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include //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 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(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(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; +}