/*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; }