From a2adaa05a69f946c3ae96f3cc0fd25d561002d50 Mon Sep 17 00:00:00 2001 From: cdez Date: Wed, 12 Mar 2025 21:09:40 -0700 Subject: [PATCH 01/32] Add lots of functionality to the server codebase. --- chatserver/server.py | 128 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 20 deletions(-) diff --git a/chatserver/server.py b/chatserver/server.py index fd79b83..dd6bb99 100644 --- a/chatserver/server.py +++ b/chatserver/server.py @@ -1,11 +1,59 @@ import socket import select +import uuid +import pickle +import os CHATPORT = 8181 BUFFER_SIZE = 1024 MAX_CONNECT_QUEUE = 10 +DATA_FILE = 'server_data.pkl' +ADMIN_USERNAME = "admin" +ADMIN_PASSWORD = "adminpass" +UUID_FILE = 'uuids.pkl' + +def save_data(client_info, chat_rooms, client_states): + serializable_client_info = {info["user_id"]: info for sock, info in client_info.items()} + serializable_chat_rooms = {info["user_id"]: chat_rooms[sock] for sock, info in client_info.items()} + serializable_client_states = {info["user_id"]: client_states[sock] for sock, info in client_info.items()} + with open(DATA_FILE, 'wb') as f: + pickle.dump((serializable_client_info, serializable_chat_rooms, serializable_client_states), f) + +def load_data(): + if os.path.exists(DATA_FILE): + with open(DATA_FILE, 'rb') as f: + serializable_client_info, serializable_chat_rooms, serializable_client_states = pickle.load(f) + client_info = {uuid.UUID(user_id): info for user_id, info in serializable_client_info.items()} + chat_rooms = {uuid.UUID(user_id): room for user_id, room in serializable_chat_rooms.items()} + client_states = {uuid.UUID(user_id): state for user_id, state in serializable_client_states.items()} + return client_info, chat_rooms, client_states + return {}, {}, {} + +def save_uuids(uuid_dict): + with open(UUID_FILE, 'wb') as f: + pickle.dump(uuid_dict, f) + +def load_uuids(): + if os.path.exists(UUID_FILE): + with open(UUID_FILE, 'rb') as f: + return pickle.load(f) + return {} + +def get_client_uuid(client_socket, uuid_dict): + client_address = client_socket.getpeername() + client_key = f"{client_address[0]}_{client_address[1]}" + + if client_key in uuid_dict: + user_id = uuid_dict[client_key] + else: + user_id = str(uuid.uuid4()) + uuid_dict[client_key] = user_id + save_uuids(uuid_dict) + + return user_id def main(): + global quitFlag # set the flag variable to true for infinite loop quitFlag = True # create a socket object @@ -23,14 +71,9 @@ def main(): # create a list to store the sockets, server and client socket_list = [server_socket] - # since a socket is associated with a username and a chatroom, - # and each one can only be associated one way at once, use dictionaries to - # associate with each other. - usernames = {} - chat_rooms = {} - # keep track of the states of each client connected, and if they are - # technically in an 'unsafe' state like in change username or chatroom mode. - client_states = {} + # load persistent data + client_info, chat_rooms, client_states = load_data() + uuid_dict = load_uuids() # let the user know the server is listening on port CHATPORT. print(f"Chatserver listening on port: {CHATPORT}") @@ -50,6 +93,9 @@ def main(): client_socket.send(b"\nUse '%!' to exit, or '%?' for help.\n") client_socket.send(b"\nType in a username: \n") client_states[client_socket] = "WAITING_FOR_USERNAME" + # Get or generate a unique user ID for the client + user_id = get_client_uuid(client_socket, uuid_dict) + client_info[client_socket] = {"user_id": user_id} # otherwise the socket is a client socket. else: # try to receive the message from the client @@ -58,14 +104,30 @@ def main(): if message: state = client_states.get(sock, "NORMAL") if state == "WAITING_FOR_USERNAME": - usernames[sock] = message.strip() - chat_rooms[sock] = "default" - sock.send(b"\nUsername accepted. You are now in the 'default' chatroom.\n") - client_states[sock] = "NORMAL" + username = message.strip() + if username == ADMIN_USERNAME: + sock.send(b"\nEnter admin password: \n") + client_states[sock] = "WAITING_FOR_ADMIN_PASSWORD" + client_info[sock]["username"] = username + else: + client_info[sock]["username"] = username + chat_rooms[sock] = "default" + sock.send(b"\nUsername accepted. You are now in the 'default' chatroom.\n") + client_states[sock] = "NORMAL" + elif state == "WAITING_FOR_ADMIN_PASSWORD": + password = message.strip() + if password == ADMIN_PASSWORD: + sock.send(b"\nAdmin authenticated successfully.\n") + client_info[sock]["is_admin"] = True + chat_rooms[sock] = "default" + client_states[sock] = "NORMAL" + else: + sock.send(b"\nInvalid admin password. Please enter a username: \n") + client_states[sock] = "WAITING_FOR_USERNAME" elif state == "WAITING_FOR_NEW_USERNAME": - usernames[sock] = message.strip() + client_info[sock]["username"] = message.strip() sock.send(b"\nUsername changed successfully.\n") - print(f"\nClient switched username to {usernames[sock]}.") + print(f"\nClient switched username to {client_info[sock]['username']}.") client_states[sock] = "NORMAL" elif state == "WAITING_FOR_NEW_CHATROOM": old_chatroom = chat_rooms[sock] @@ -79,7 +141,7 @@ def main(): if message.strip() == "%!": sock.close() socket_list.remove(sock) - del usernames[sock] + del client_info[sock] del chat_rooms[sock] del client_states[sock] print("\nClient has closed connection.") @@ -92,14 +154,38 @@ def main(): sock.send(b"\t%! : Exit from the Chat Server.\n") sock.send(b"\t%* : Switch to another chatroom.\n") sock.send(b"\t%$ : Change Username.\n") + sock.send(b"\t%@ : Send a direct message to a user.\n") + if client_info[sock].get("is_admin"): + sock.send(b"\t%stop : Stop the server.\n") elif message.strip() == "%*": sock.send(b"\nEnter chatroom name: \n") client_states[sock] = "WAITING_FOR_NEW_CHATROOM" elif message.strip() == "%$": sock.send(b"\nEnter new username: \n") client_states[sock] = "WAITING_FOR_NEW_USERNAME" + elif message.strip().startswith("%@ "): + parts = message.strip().split(' ', 2) + if len(parts) < 3: + sock.send(b"\nInvalid direct message format. Use: %@ \n") + else: + target_username = parts[1] + dm_message = parts[2] + target_sock = None + for s, info in client_info.items(): + if info.get("username") == target_username: + target_sock = s + break + if target_sock: + sender_username = client_info[sock]["username"] + target_sock.send(f"DM from {sender_username}: {dm_message}\n".encode('utf-8')) + sock.send(b"\nDirect message sent.\n") + else: + sock.send(b"\nUser not found.\n") + elif message.strip() == "%stop" and client_info[sock].get("is_admin"): + print("\nAdmin has stopped the server.") + quitFlag = False else: - username = usernames[sock] + username = client_info[sock]["username"] chat_room = chat_rooms[sock] send_message = f"{username} >> {message}".encode('utf-8') for client_sock in socket_list: @@ -108,8 +194,8 @@ def main(): else: sock.close() socket_list.remove(sock) - if sock in usernames: - del usernames[sock] + if sock in client_info: + del client_info[sock] if sock in chat_rooms: old_chatroom = chat_rooms[sock] del chat_rooms[sock] @@ -122,8 +208,8 @@ def main(): print(f"Error: {err}") sock.close() socket_list.remove(sock) - if sock in usernames: - del usernames[sock] + if sock in client_info: + del client_info[sock] if sock in chat_rooms: old_chatroom = chat_rooms[sock] del chat_rooms[sock] @@ -133,6 +219,8 @@ def main(): del client_states[sock] print("\nClient has closed connection.") + # Save data before closing the server + save_data(client_info, chat_rooms, client_states) server_socket.close() if __name__ == "__main__": From 12eb4fb3f5b3b639402fca7d79d9feec68988728 Mon Sep 17 00:00:00 2001 From: cdez Date: Thu, 13 Mar 2025 16:05:26 -0700 Subject: [PATCH 02/32] Create the client, and modify the server. --- chatserver/client.py | 47 ++++++++++++++++++++++++++++++++++++++++++++ chatserver/server.py | 12 +++++------ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/chatserver/client.py b/chatserver/client.py index e69de29..35bb3ff 100644 --- a/chatserver/client.py +++ b/chatserver/client.py @@ -0,0 +1,47 @@ +import socket +import sys +import select + +SERVER_HOST = 'localhost' +SERVER_PORT = 8181 +BUFFER_SIZE = 1024 + +def main(): + # create a socket object + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + # connect to the server + client_socket.connect((SERVER_HOST, SERVER_PORT)) + print(f"Connected to chat server at {SERVER_HOST}:{SERVER_PORT}") + + while True: + # use select to wait for either server messages or user input + read_sockets, _, _ = select.select([client_socket, sys.stdin], [], []) + + for sock in read_sockets: + if sock == client_socket: + # receive message from server + server_message = client_socket.recv(BUFFER_SIZE).decode('utf-8') + if not server_message: + print("Disconnected from server") + return + print(f"\n{server_message}") + print("> ", end="", flush=True) + else: + # prompt for user input + client_message = input() + if client_message.strip() == "%!": + print("Exiting chat...") + return + + # send message to server + client_socket.send(client_message.encode('utf-8')) + + except Exception as e: + print(f"Error: {e}") + finally: + client_socket.close() + print("Connection closed") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/chatserver/server.py b/chatserver/server.py index fd79b83..ee1d18b 100644 --- a/chatserver/server.py +++ b/chatserver/server.py @@ -48,7 +48,7 @@ def main(): print("\nAccepted connection from new client.") client_socket.send(b"\nWelcome to the chat server!\n") client_socket.send(b"\nUse '%!' to exit, or '%?' for help.\n") - client_socket.send(b"\nType in a username: \n") + client_socket.send(b"\nType in a username: ") client_states[client_socket] = "WAITING_FOR_USERNAME" # otherwise the socket is a client socket. else: @@ -60,17 +60,17 @@ def main(): if state == "WAITING_FOR_USERNAME": usernames[sock] = message.strip() chat_rooms[sock] = "default" - sock.send(b"\nUsername accepted. You are now in the 'default' chatroom.\n") + sock.send(b"\nUsername accepted. You are now in the 'default' chatroom.") client_states[sock] = "NORMAL" elif state == "WAITING_FOR_NEW_USERNAME": usernames[sock] = message.strip() - sock.send(b"\nUsername changed successfully.\n") + sock.send(b"\nUsername changed successfully.") print(f"\nClient switched username to {usernames[sock]}.") client_states[sock] = "NORMAL" elif state == "WAITING_FOR_NEW_CHATROOM": old_chatroom = chat_rooms[sock] chat_rooms[sock] = message.strip() - sock.send(f"\nSwitched to chatroom '{chat_rooms[sock]}.'\n".encode('utf-8')) + sock.send(f"\nSwitched to chatroom '{chat_rooms[sock]}.'".encode('utf-8')) print(f"\nClient switched to chatroom '{chat_rooms[sock]}.'") if old_chatroom != "default" and old_chatroom not in chat_rooms.values(): print(f"\nChatroom '{old_chatroom}' is empty and removed.") @@ -93,10 +93,10 @@ def main(): sock.send(b"\t%* : Switch to another chatroom.\n") sock.send(b"\t%$ : Change Username.\n") elif message.strip() == "%*": - sock.send(b"\nEnter chatroom name: \n") + sock.send(b"\nEnter chatroom name: ") client_states[sock] = "WAITING_FOR_NEW_CHATROOM" elif message.strip() == "%$": - sock.send(b"\nEnter new username: \n") + sock.send(b"\nEnter new username: ") client_states[sock] = "WAITING_FOR_NEW_USERNAME" else: username = usernames[sock] From fad9f50ff80e6bfe01f4fdbd54f415d9d77440bf Mon Sep 17 00:00:00 2001 From: cdez Date: Thu, 13 Mar 2025 17:15:15 -0700 Subject: [PATCH 03/32] Fix the client to work regardless of the IP/Port combination, default to localhost and 8181 if nothing is provided. --- chatserver/client.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/chatserver/client.py b/chatserver/client.py index 35bb3ff..9ad3c98 100644 --- a/chatserver/client.py +++ b/chatserver/client.py @@ -2,11 +2,19 @@ import socket import sys import select -SERVER_HOST = 'localhost' -SERVER_PORT = 8181 BUFFER_SIZE = 1024 +DEFAULT_HOST = 'localhost' +DEFAULT_PORT = 8181 def main(): + if len(sys.argv) != 3: + SERVER_HOST = input(f"Enter server host (default: {DEFAULT_HOST}): ") or DEFAULT_HOST + SERVER_PORT = input(f"Enter server port (default: {DEFAULT_PORT}): ") or DEFAULT_PORT + SERVER_PORT = int(SERVER_PORT) + else: + SERVER_HOST = sys.argv[1] + SERVER_PORT = int(sys.argv[2]) + # create a socket object client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: From 0b9e4a3ee4d2b690fff51afce7c17e0b30f56416 Mon Sep 17 00:00:00 2001 From: cdez Date: Sun, 16 Mar 2025 19:51:00 -0700 Subject: [PATCH 04/32] Add the last bits of the assignment. --- chatserver/README.md | 4 ++-- chatserver/demo.pcap | Bin 0 -> 3848 bytes 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 chatserver/demo.pcap diff --git a/chatserver/README.md b/chatserver/README.md index ac65127..3a2caaa 100644 --- a/chatserver/README.md +++ b/chatserver/README.md @@ -1,8 +1,8 @@ # Running the chatserver -Use `python server.py` to run the server +Use `python3 server.py` to run the server ## chatserver communication -Use the client to communicate with the chatserver by running `python client.py` +Use the client to communicate with the chatserver by running `python3 client.py` ## chatserver information Included is the server and client written in Python. diff --git a/chatserver/demo.pcap b/chatserver/demo.pcap new file mode 100644 index 0000000000000000000000000000000000000000..ba4013ee064bdafc5c750f841e1d1252e4df3541 GIT binary patch literal 3848 zcmb7{UrbwN7{Bdh}k%QA_$@R4ZwBh*EsVvu zNn~O1#+Y4jy|~@z#S-VPbY^R^EG*%|5aOK|n236_nM_RZ9XaO!Y4^cPzeCIO zKJV{+&qwR7s&ZcyLJS^x`FR%}*VQT!72>sIGOo!%f2n8@iE!lYcclNI_kcIhCPy-{ zcvJ>D`h$Mi+#gO&$0l%v=X@>DC=&tEpGhPm;iPOn9ZPAGW8u_zi|p{?OEypCAKQKH z=QQ7luhZ{87!7nr_=i~ZLLX^f-ou5T3YUMRk%IbZ^C@83ma91TwvuPrToavgc9Z|>QgvHJU1@Y)cuibMsdnN^{GeiR^2^C=qm3w=-sMo zdNy^>Lz|0!zk}{6>>0}MNvB_%ZXNuhIrMAlx=(BIh@Q}7T9@fDO-9DTX*sE-rnFQe zrbRHHv!2Bw>QVhBr>wOe^0#uj3Vklt^CNU8v1h1y^lj?N<~gyRh@+lUlbURLsj-O# zY3F0|NIxrEBfc&X4ye@BMvwB)WjGSil4&jKmG9{p8BS?( zLZ4=1IEqbCZ8V&Tr_SySWf5DS|xN3T~ zp2dM&qTpF<(`A1!8OE7l=o)h+ySt@6Zl1-=#YHZ?$Wh3<;=K>N2kgAbrT&8HeK@ab z=uUpR5PB8Y48^;18{UR4dAFOqSMF0cqU-TS{QWS($^2{R7xJyx41jsPow?_%C%bar z^gRZBF7h}zw-7ppJwy5KbNaUFlKEkixuLtk+^B&1PXD9GQ^>#KUJvd=@V~Y(=j7u# zYx=j&%&j{QLNF{C%72g3zfHFex(7_|s}0m0!+p#9cSg_r|Noo0xX7sVhX)0{D@H?L z@3FHF-1cNw8%*y>LpHDbA#?|E%~0NtJH6X<$sPk{%)6nx!QNU0-YP-U*@G)Z)S*~; z(1V!02V@nvZPsBuYbtmB96Ex03{^+aS%*!RJ!ms~u(nLybGjbg>a2Mm+7K(^f5B`F z>|5>ZgX5m;+Op}{I`@A<_jTwpl;@*P&x*aFOZGTH4fcla279^}Vyn5|U0K+_V$%xd z({|=$6C5}F55p@LXY#M_b0G}6U?~4xPX9JtGJnNn{^&7vM`kpM*z&wNbSGCV_i1qV z+qw77d$Nxno4(^TjYZ$zLKj0W7|M74Eq5=5mG89~io2mp?l?&e?uPCLce4T^n8P{m z%SF_n82Q0|-p-zkdgsj=_{hZ?j@2}G1+izS8aBN@vFX-Y&$6eNx6}Qs2wlZy9?S!F z<|p=hvQIB><@dST!hUVKwP44x)nbN0XQ7wvTl~LmZCqK8c7rPU`9_Hu8{5^4euB#0D7oo?s1AFUYj{g8= CVqeDq literal 0 HcmV?d00001 From f3c913c97c6ff4d37dad5181e804c960acf06e33 Mon Sep 17 00:00:00 2001 From: cdez Date: Wed, 19 Mar 2025 17:52:34 -0700 Subject: [PATCH 05/32] This branch has the chatserverplus stuff in it, copied over the client, removed the unnecessary files included. --- chatserver/README.md | 6 +- chatserver/client.py | 55 +++++++++++++++ echoserver/Makefile | 20 ------ echoserver/README.md | 10 --- echoserver/echoserver.c | 150 ---------------------------------------- echoserver/echoserver.h | 25 ------- timeserver/Makefile | 20 ------ timeserver/README.md | 16 ----- timeserver/demo.pcap | Bin 54532 -> 0 bytes timeserver/timeserver.c | 83 ---------------------- timeserver/timeserver.h | 23 ------ 11 files changed, 56 insertions(+), 352 deletions(-) delete mode 100644 echoserver/Makefile delete mode 100644 echoserver/README.md delete mode 100644 echoserver/echoserver.c delete mode 100644 echoserver/echoserver.h delete mode 100644 timeserver/Makefile delete mode 100644 timeserver/README.md delete mode 100644 timeserver/demo.pcap delete mode 100644 timeserver/timeserver.c delete mode 100644 timeserver/timeserver.h diff --git a/chatserver/README.md b/chatserver/README.md index ac65127..47301bf 100644 --- a/chatserver/README.md +++ b/chatserver/README.md @@ -5,8 +5,4 @@ Use `python server.py` to run the server Use the client to communicate with the chatserver by running `python client.py` ## chatserver information -Included is the server and client written in Python. - -## pcap information -A demonstration of the chatserver has been captured with Wireshark in the -included `demo.pcap` file. +Included is the server and client written in Python. \ No newline at end of file diff --git a/chatserver/client.py b/chatserver/client.py index e69de29..9ad3c98 100644 --- a/chatserver/client.py +++ b/chatserver/client.py @@ -0,0 +1,55 @@ +import socket +import sys +import select + +BUFFER_SIZE = 1024 +DEFAULT_HOST = 'localhost' +DEFAULT_PORT = 8181 + +def main(): + if len(sys.argv) != 3: + SERVER_HOST = input(f"Enter server host (default: {DEFAULT_HOST}): ") or DEFAULT_HOST + SERVER_PORT = input(f"Enter server port (default: {DEFAULT_PORT}): ") or DEFAULT_PORT + SERVER_PORT = int(SERVER_PORT) + else: + SERVER_HOST = sys.argv[1] + SERVER_PORT = int(sys.argv[2]) + + # create a socket object + client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + # connect to the server + client_socket.connect((SERVER_HOST, SERVER_PORT)) + print(f"Connected to chat server at {SERVER_HOST}:{SERVER_PORT}") + + while True: + # use select to wait for either server messages or user input + read_sockets, _, _ = select.select([client_socket, sys.stdin], [], []) + + for sock in read_sockets: + if sock == client_socket: + # receive message from server + server_message = client_socket.recv(BUFFER_SIZE).decode('utf-8') + if not server_message: + print("Disconnected from server") + return + print(f"\n{server_message}") + print("> ", end="", flush=True) + else: + # prompt for user input + client_message = input() + if client_message.strip() == "%!": + print("Exiting chat...") + return + + # send message to server + client_socket.send(client_message.encode('utf-8')) + + except Exception as e: + print(f"Error: {e}") + finally: + client_socket.close() + print("Connection closed") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/echoserver/Makefile b/echoserver/Makefile deleted file mode 100644 index 221b29f..0000000 --- a/echoserver/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -CC = gcc -Wall -std=c11 -pedantic -SOURCES = echoserver.c -OBJECTS = echoserver.o - -all: echoserver - -echoserver: $(OBJECTS) - $(CC) $(OBJECTS) -o echoserver - -echoserver.o: echoserver.c - $(CC) -c echoserver.c -o echoserver.o - -clean: - rm -f $(OBJECTS) echoserver echoserver.out - -clean_objects: - rm -f $(OBJECTS) - -run: - ./echoserver diff --git a/echoserver/README.md b/echoserver/README.md deleted file mode 100644 index fb81333..0000000 --- a/echoserver/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Running the echoserver -Use `make` to compile the echoserver. -Use `make run` to run the echoserver. This will run on an infinite loop. -Use `Ctrl+C` to stop the echoserver. - -## echoserver communication -Communicating with the server can happen using telnet in TCP mode. - -## echoserver information -The echoserver runs on TCP port 8888. \ No newline at end of file diff --git a/echoserver/echoserver.c b/echoserver/echoserver.c deleted file mode 100644 index 73e2bbc..0000000 --- a/echoserver/echoserver.c +++ /dev/null @@ -1,150 +0,0 @@ -// header files -#include "echoserver.h" - -// constants -// echoserver port is 8888 -#define ECHOPORT 8888 -#define BUFFER_SIZE 1024 -#define MAX_CONNECT_QUEUE 10 - -// prototypes - -// main function -int main(int argc, char **argv) -{ - // initialize program (variables and structs) - bool quitFlag = true; - int socketEchoFD; - int clientEchoFD; - int reuseAddrOpt = 1; - int keepAliveOpt = 1; - char message[BUFFER_SIZE]; - int messageBytes; - struct sockaddr_in serverAddr; - struct sockaddr_storage clientAddr; - socklen_t clientSize; - memset(&serverAddr, 0, sizeof(serverAddr)); - memset(&clientAddr, 0, sizeof(clientAddr)); - // define a TCP socket - socketEchoFD = socket(AF_INET, SOCK_STREAM, 0); - if(socketEchoFD < 0) - { - printf("Unable to create the socket.\n"); - return -1; - } - // use the setsockopt function to make the socket accept different configurations - // allow for the reuse of the address/bind. - setsockopt(socketEchoFD, SOL_SOCKET, SO_REUSEADDR, &reuseAddrOpt, sizeof(reuseAddrOpt)); - // also need to set the socket to keepalive using setsockopt - setsockopt(socketEchoFD, SOL_SOCKET, SO_KEEPALIVE, &keepAliveOpt, sizeof(keepAliveOpt)); - // bind the TCP socket to port 8888 / ECHOPORT - serverAddr.sin_family = AF_INET; - serverAddr.sin_addr.s_addr = INADDR_ANY; - serverAddr.sin_port = htons(ECHOPORT); - int bindCheck = bind(socketEchoFD, &serverAddr, sizeof(serverAddr)); - if(bindCheck < 0) - { - printf("Unable to bind the socket to the port or address.\n Is either in use?"); - return -1; - } - // use fnctl to set the non-blocking flag for the socket - fcntl(socketEchoFD, F_SETFL, O_NONBLOCK); - // the server will now accept client connections by listening. - listen(socketEchoFD, MAX_CONNECT_QUEUE); - // create a file descriptor set, to keep track of the sockets in use using fd_set and FD_ZERO? - fd_set socketList; - FD_ZERO(&socketList); - // add the socketEchoFD to the file descriptor set using fd_set - FD_SET(socketEchoFD, &socketList); - // max file descriptor, this will be used to 'find' the socket currently connected - int maxFD = socketEchoFD; - // while loop that will continue to listen for messages from clients. - printf("Echoserver listening on port: %d\n", ECHOPORT); - while (quitFlag) - { - // set of active connections - fd_set activeSet = socketList; - // use the select() function here to check the set of sockets - int selectedFD = select(maxFD+1, &activeSet, NULL, NULL, NULL); - // check to make sure select() didn't error out. - if(selectedFD == -1) - { - // - printf("\nUnable to select socket to listen to.\n"); - return -1; - } - // for loop that loops up to the max file descriptor. - for(int fileIter = 0; fileIter <= maxFD; fileIter++) - { - // check to see if the file descriptor is in the set of sockets using FD_ISSET() - if(FD_ISSET(fileIter, &activeSet)) - { - // if the server socket is being written to/read from, - if(fileIter == socketEchoFD) - { - // create a client socket using accept - clientSize = sizeof(clientAddr); - clientEchoFD = accept(socketEchoFD, &clientAddr, &clientSize); - // check to see if the client socket is connected (-1) - if(clientEchoFD == -1) - { - printf("\nCould not connect the client to a socket.\n"); - return -1; - } - else - { - // set the non blocking flag using fnctl - fcntl(clientEchoFD, F_SETFL, O_NONBLOCK); - // add the client socket to the set of file descriptor sockets - FD_SET(clientEchoFD, &socketList); - // check to see if the new socket FD is greater than the current max (clientSocket > maxFD) - if(clientEchoFD > maxFD) - { - // if it is, set the new max. - maxFD = clientEchoFD; - } - printf("\nConnected client to socket.\n"); - } - printf("\nAccepted connection from new client.\n"); - } - // if the client socket(s) are being written to/read from, - else - { - // grab the data from the client, save to message. - messageBytes = recv(fileIter, message, BUFFER_SIZE, 0); - // make sure that the connection is still open before attempting to send. - if(messageBytes <= 0) // old: messageBytes <= 0 - { - // if it is not open, close the socket and remove the socket from the socket list using FD_CLR() - close(fileIter); - FD_CLR(fileIter, &socketList); - printf("\nClient has closed connection.\n"); - } - else - { - // if it is open, send the message back to the client over the client socket - send(fileIter, message, messageBytes, 0); - printf("\nMessage has been sent back to client.\n"); - } - } - } - } - } - // close the socket - close(socketEchoFD); - // profit??? - return 0; -} - -// function definitions - -/* -Name: ??? -Process: ??? -Function Input/Parameters: ??? -Function Output/Parameters: ??? -Function Output/Returned: ??? -Device Input/Device: ??? -Device Output/Device: ??? -Dependencies: ??? -*/ \ No newline at end of file diff --git a/echoserver/echoserver.h b/echoserver/echoserver.h deleted file mode 100644 index b1ef31c..0000000 --- a/echoserver/echoserver.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef ECHOSERVER -#define ECHOSERVER - -// included libraries -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// data structures - -// function prototypes - -#endif // ECHOSERVER \ No newline at end of file diff --git a/timeserver/Makefile b/timeserver/Makefile deleted file mode 100644 index 6f7d8b7..0000000 --- a/timeserver/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -CC = gcc -Wall -std=c11 -pedantic -SOURCES = timeserver.c -OBJECTS = timeserver.o - -all: timeserver - -timeserver: $(OBJECTS) - $(CC) $(OBJECTS) -o timeserver - -timeserver.o: timeserver.c - $(CC) -c timeserver.c -o timeserver.o - -clean: - rm -f $(OBJECTS) timeserver timeserver.out - -clean_objects: - rm -f $(OBJECTS) - -run: - ./timeserver diff --git a/timeserver/README.md b/timeserver/README.md deleted file mode 100644 index b9d8b6a..0000000 --- a/timeserver/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Running the timeserver -Use `make` to compile the timeserver. -Use `make run` to run the timeserver. This will run on an infinite loop. -Use `Ctrl+C` to stop the timeserver. - -## timeserver communication -Communicating with the server can happen using netcat in UDP mode (-u flag). - -## timeserver information -The timeserver runs on UDP port 8888. -The timeserver will wait for `time\r\n` to be sent on this port, and if received, -will send back a response of the time (in ms) since January 1st, 1970 (UNIX Epoch). - -## pcap information -A demonstration of the timeserver has been captured with Wireshark in the -included `demo.pcap` file. \ No newline at end of file diff --git a/timeserver/demo.pcap b/timeserver/demo.pcap deleted file mode 100644 index 6c48bde8ec57a25a305c881fb47c3ae03d1ad8e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54532 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 P3o$~>Vg<610mu#j1EFy= diff --git a/timeserver/timeserver.c b/timeserver/timeserver.c deleted file mode 100644 index 6eef908..0000000 --- a/timeserver/timeserver.c +++ /dev/null @@ -1,83 +0,0 @@ -// header files -#include "timeserver.h" - -// constants -// timeserver port is 8888 -#define TIMEPORT 8888 -#define BUFFER_SIZE 1024 - -// prototypes - -// main function -int main(int argc, char **argv) -{ - // initialize program (variables and structs) - bool quitFlag = true; - int socketTimeFD; - char message[BUFFER_SIZE]; - struct sockaddr_in serverAddr, clientAddr; - memset(&serverAddr, 0, sizeof(serverAddr)); - memset(&clientAddr, 0, sizeof(clientAddr)); - // define a UDP socket - socketTimeFD = socket(AF_INET, SOCK_DGRAM, 0); - if(socketTimeFD < 0) - { - printf("Unable to create the socket."); - return -1; - } - // bind the UDP socket to port 8888 / TIMEPORT - serverAddr.sin_family = AF_INET; - serverAddr.sin_addr.s_addr = INADDR_ANY; - serverAddr.sin_port = htons(TIMEPORT); - int bindCheck = bind(socketTimeFD, &serverAddr, sizeof(serverAddr)); - if(bindCheck < 0) - { - printf("Unable to bind the socket to the port or address.\n Is either in use?"); - return -1; - } - // while loop to see if the "time\r\n" has been received (recv call?) - printf("Timeserver listening on port: %d\n", TIMEPORT); - while(quitFlag) - { - int clientAddrLen; - int receivedDataLen; - // grab the data from the client - receivedDataLen = recvfrom(socketTimeFD, message, BUFFER_SIZE, MSG_WAITALL, &clientAddr, &clientAddrLen); - message[receivedDataLen] = '\0'; - // if "time\r\n" is received, then send() back the linux epoch. - if(strcmp(message, "time\n") == 0 || strcmp(message, "time\r\n") == 0) - { - // setup the reply message - char reply[BUFFER_SIZE]; - // add the current time to the reply - sprintf(reply, "%d", time(NULL)); - // send the reply to the requestee - sendto(socketTimeFD, (const char *)reply, strlen(reply), 0, (const struct sockaddr *) &clientAddr, clientAddrLen); - // log the time command recv'd - printf("\nServer received time. Replied with: %d\n", time(NULL)); - } - else if (strcmp(message, "close\n") == 0 || strcmp(message, "quit\n") == 0 || strcmp(message, "q\n") == 0) - { - // if the server recv'd the close command, then close the server. - printf("\nServer received close.\n"); - quitFlag = false; - } - } - // close the socket - close(socketTimeFD); - // profit??? - return 0; -} - -// function definitions - -/* -Name: ??? -Process: ??? -Function Input/Parameters: ??? -Function Output/Parameters: ??? -Function Output/Returned: ??? -Device Input/Device: ??? -Device Output/Device: ??? -Dependencies: ??? -*/ \ No newline at end of file diff --git a/timeserver/timeserver.h b/timeserver/timeserver.h deleted file mode 100644 index 1a8135b..0000000 --- a/timeserver/timeserver.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TIMESERVER -#define TIMESERVER - -// included libraries -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// data structures - -// function prototypes - -#endif // TIMESERVER \ No newline at end of file From aad3ecd8040e47b1e2b4b471782ca305e2039ed1 Mon Sep 17 00:00:00 2001 From: cdez Date: Wed, 19 Mar 2025 18:38:20 -0700 Subject: [PATCH 06/32] Allow for both Windows and Linux to use the same 'client' --- chatserver/client.py | 60 ++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/chatserver/client.py b/chatserver/client.py index 9ad3c98..74ccf2b 100644 --- a/chatserver/client.py +++ b/chatserver/client.py @@ -1,6 +1,7 @@ import socket import sys import select +import os BUFFER_SIZE = 1024 DEFAULT_HOST = 'localhost' @@ -22,29 +23,50 @@ def main(): client_socket.connect((SERVER_HOST, SERVER_PORT)) print(f"Connected to chat server at {SERVER_HOST}:{SERVER_PORT}") - while True: - # use select to wait for either server messages or user input - read_sockets, _, _ = select.select([client_socket, sys.stdin], [], []) - - for sock in read_sockets: - if sock == client_socket: - # receive message from server + if os.name == 'nt': # Windows-specific logic + import msvcrt + client_socket.setblocking(False) # Set the socket to non-blocking mode + + while True: + # Check for server messages + try: server_message = client_socket.recv(BUFFER_SIZE).decode('utf-8') - if not server_message: - print("Disconnected from server") - return - print(f"\n{server_message}") - print("> ", end="", flush=True) - else: - # prompt for user input - client_message = input() + if server_message: + print(f"\n{server_message}") + print("> ", end="", flush=True) + except BlockingIOError: + pass + + # Check for user input + if msvcrt.kbhit(): + client_message = input("> ") if client_message.strip() == "%!": print("Exiting chat...") - return - - # send message to server + break client_socket.send(client_message.encode('utf-8')) - + + else: # Linux/Unix-specific logic + while True: + # use select to wait for either server messages or user input + read_sockets, _, _ = select.select([client_socket, sys.stdin], [], []) + + for sock in read_sockets: + if sock == client_socket: + # receive message from server + server_message = client_socket.recv(BUFFER_SIZE).decode('utf-8') + if not server_message: + print("Disconnected from server") + return + print(f"\n{server_message}") + print("> ", end="", flush=True) + else: + # prompt for user input + client_message = input() + if client_message.strip() == "%!": + print("Exiting chat...") + return + client_socket.send(client_message.encode('utf-8')) + except Exception as e: print(f"Error: {e}") finally: From cbcb35a1587a1f4d3a9bfba5fe0262925044faf0 Mon Sep 17 00:00:00 2001 From: cdez Date: Thu, 27 Mar 2025 11:32:27 -0700 Subject: [PATCH 07/32] Add all of the homework 4 files, all of the graphs made, the README, and the tcp-variants-comparison file. --- networksim/CS460Homework4_CBvCB_graph.png | Bin 0 -> 72350 bytes networksim/CS460Homework4_Cubic_graph.png | Bin 0 -> 46473 bytes networksim/CS460Homework4_NRvCB_graph.png | Bin 0 -> 70374 bytes networksim/CS460Homework4_NRvNR_graph.png | Bin 0 -> 54944 bytes networksim/CS460Homework4_NewReno_graph.png | Bin 0 -> 35136 bytes networksim/CS460Homework4_graph.png | Bin 0 -> 73931 bytes networksim/CS460Homework4_graph_5p.png | Bin 0 -> 113285 bytes networksim/README.md | 7 + networksim/tcp-variants-comparison.cc | 624 ++++++++++++++++++++ 9 files changed, 631 insertions(+) create mode 100644 networksim/CS460Homework4_CBvCB_graph.png create mode 100644 networksim/CS460Homework4_Cubic_graph.png create mode 100644 networksim/CS460Homework4_NRvCB_graph.png create mode 100644 networksim/CS460Homework4_NRvNR_graph.png create mode 100644 networksim/CS460Homework4_NewReno_graph.png create mode 100644 networksim/CS460Homework4_graph.png create mode 100644 networksim/CS460Homework4_graph_5p.png create mode 100644 networksim/README.md create mode 100644 networksim/tcp-variants-comparison.cc diff --git a/networksim/CS460Homework4_CBvCB_graph.png b/networksim/CS460Homework4_CBvCB_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..0005c3ad4f95eb8c8bd128bccbb2561c82c9743e GIT binary patch literal 72350 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