diff --git a/chatserver/README.md b/chatserver/README.md index 3a2caaa..47301bf 100644 --- a/chatserver/README.md +++ b/chatserver/README.md @@ -1,12 +1,8 @@ # Running the chatserver -Use `python3 server.py` to run the server +Use `python server.py` to run the server ## chatserver communication -Use the client to communicate with the chatserver by running `python3 client.py` +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 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: diff --git a/chatserver/demo.pcap b/chatserver/demo.pcap deleted file mode 100644 index ba4013e..0000000 Binary files a/chatserver/demo.pcap and /dev/null differ diff --git a/chatserver/server.py b/chatserver/server.py index ee1d18b..40978b3 100644 --- a/chatserver/server.py +++ b/chatserver/server.py @@ -1,76 +1,419 @@ import socket import select +import uuid +import pickle +import os +import threading +from flask import Flask, request, jsonify +from flask_socketio import SocketIO, emit, join_room, leave_room +from flask_cors import CORS +from datetime import datetime # Import for timestamps +import markdown # Import the markdown library + +# Flask app for REST API and WebSocket +app = Flask(__name__) +socketio = SocketIO(app, cors_allowed_origins="*") # Allow all origins + +# Enable CORS for the Flask app +CORS(app) CHATPORT = 8181 BUFFER_SIZE = 1024 MAX_CONNECT_QUEUE = 10 +DATA_FILE = 'server_data.pkl' +UUID_FILE = 'uuids.pkl' -def main(): - # set the flag variable to true for infinite loop - quitFlag = True - # create a socket object +# Global variables for chat server +socket_list = [] +client_info = {} +chat_rooms = {} +client_states = {} +uuid_dict = {} +message_history = {} # New: Store message history for each chat room +server_socket = None +quitFlag = True + +# Utility functions +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 handle_message(username, chat_room, message): + """Store the message in history and broadcast it to all clients.""" + # Add a timestamp to the message + timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # Convert the message to HTML using Markdown + html_message = markdown.markdown(message) + + # Store the message in the chat room's history + if chat_room not in message_history: + message_history[chat_room] = [] + message_history[chat_room].append(f"[{timestamp}] {username}: {message}") + + # Broadcast the message to WebSocket clients in the chat room + socketio.emit('message', { + 'username': username, + 'message': html_message, # Send the HTML-rendered message + 'timestamp': timestamp, + 'chat_room': chat_room + }, room=chat_room) + + # Broadcast the message to socket-based clients in the chat room + send_message = f"[{timestamp}] {username} >> {message}".encode('utf-8') + for client_sock in socket_list: + if client_sock != server_socket and chat_rooms.get(client_sock) == chat_room: + client_sock.send(send_message) + +def handle_command(username, chat_room, command, sock=None): + """Process commands sent by clients.""" + if command.strip() == "%?": + help_message = ( + "Help: (%?)\n" + "Send messages like normal. Use enter to send a message.\n" + "Available Commands:\n" + "\t%? : Show this help prompt.\n" + "\t%! : Exit from the Chat Server.\n" + "\t%* : Switch to another chatroom.\n" + "\t%$ : Change Username.\n" + "\t%@ : Send a direct message to a user.\n" + ) + if sock: + sock.send(help_message.encode('utf-8')) + else: + socketio.emit('message', {'username': 'Server', 'message': help_message}, room=chat_room) + return + + if command.strip() == "%*": + if sock: + sock.send(b"\nEnter chatroom name: \n") + client_states[sock] = "WAITING_FOR_NEW_CHATROOM" + else: + socketio.emit('message', {'username': 'Server', 'message': "Enter chatroom name:"}, room=chat_room) + return + + if command.strip() == "%$": + if sock: + sock.send(b"\nEnter new username: \n") + client_states[sock] = "WAITING_FOR_NEW_USERNAME" + else: + socketio.emit('message', {'username': 'Server', 'message': "Enter new username:"}, room=chat_room) + return + + if command.strip().startswith("%@ "): + parts = command.strip().split(' ', 2) + if len(parts) < 3: + error_message = "Invalid direct message format. Use: %@ " + if sock: + sock.send(f"\n{error_message}\n".encode('utf-8')) + else: + socketio.emit('message', {'username': 'Server', 'message': error_message}, room=chat_room) + else: + target_username = parts[1] + dm_message = parts[2] + timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # Check if the target is a socket-based client + target_sock = None + for s, info in client_info.items(): + if not info.get("is_websocket") and info.get("username") == target_username: + target_sock = s + break + + # Check if the target is a WebSocket client + target_websocket_sid = None + for sid, info in client_info.items(): + if info.get("is_websocket") and info.get("username") == target_username: + target_websocket_sid = sid + break + + if target_sock: + # Send the direct message to the socket-based client + sender_username = username + target_sock.send(f"[{timestamp}] DM from {sender_username}: {dm_message}\n".encode('utf-8')) + if sock: + sock.send(f"[{timestamp}] Direct message sent.\n".encode('utf-8')) + elif target_websocket_sid: + # Send the direct message to the WebSocket client + socketio.emit('message', { + 'username': username, + 'message': dm_message, + 'timestamp': timestamp, + 'chat_room': 'Direct Message' + }, to=target_websocket_sid) + if sock: + sock.send(f"[{timestamp}] Direct message sent.\n".encode('utf-8')) + else: + # Target user not found + error_message = "User not found." + if sock: + sock.send(f"\n{error_message}\n".encode('utf-8')) + else: + socketio.emit('message', {'username': 'Server', 'message': error_message}, room=chat_room) + return + + if command.strip() == "%stop": + if client_info.get(sock, {}).get("is_admin"): + print("\nAdmin has stopped the server.") + global quitFlag + quitFlag = False + if sock: + sock.send(b"\nServer is stopping...\n") + else: + socketio.emit('message', {'username': 'Server', 'message': "Server is stopping..."}, room=chat_room) + else: + error_message = "You do not have permission to stop the server." + if sock: + sock.send(f"\n{error_message}\n".encode('utf-8')) + else: + socketio.emit('message', {'username': 'Server', 'message': error_message}, room=chat_room) + return + +# REST API Endpoints +@app.route('/send_message', methods=['POST']) +def send_message(): + """Send a message to a specific chat room.""" + data = request.json + username = data.get('username') + chat_room = data.get('chat_room', 'default') + message = data.get('message') + + if not username or not message: + return jsonify({"error": "Username and message are required"}), 400 + + if message.strip().startswith("%"): + handle_command(username, chat_room, message) + else: + handle_message(username, chat_room, message) + + return jsonify({"status": "Message sent and processed"}), 200 + +@app.route('/create_chatroom', methods=['POST']) +def create_chatroom(): + """Create a new chat room.""" + data = request.json + chat_room = data.get('chat_room') + + if not chat_room: + return jsonify({"error": "Chat room name is required"}), 400 + + # Add logic to create a chat room (if needed) + return jsonify({"status": f"Chat room '{chat_room}' created"}), 200 + +@app.route('/get_messages', methods=['GET']) +def get_messages(): + """Retrieve messages for a specific user (not implemented here).""" + # You can implement a message queue or buffer for each user to store messages + return jsonify({"error": "Not implemented"}), 501 + +@app.route('/get_history', methods=['GET']) +def get_history(): + """Retrieve message history for a specific chat room.""" + chat_room = request.args.get('chat_room', 'default') + if chat_room not in message_history: + return jsonify({"messages": []}) # Return an empty list if no history exists + + # Return the full message history with timestamps + return jsonify({"messages": message_history[chat_room]}) + +@app.route('/send_direct_message', methods=['POST']) +def send_direct_message(): + """Send a direct message to a specific user.""" + data = request.json + sender_username = data.get('sender_username') + target_username = data.get('target_username') + message = data.get('message') + + if not sender_username or not target_username or not message: + return jsonify({"error": "Sender username, target username, and message are required"}), 400 + + timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # Check if the target is a socket-based client + target_sock = None + for s, info in client_info.items(): + if not info.get("is_websocket") and info.get("username") == target_username: + target_sock = s + break + + # Check if the target is a WebSocket client + target_websocket_sid = None + for sid, info in client_info.items(): + if info.get("is_websocket") and info.get("username") == target_username: + target_websocket_sid = sid + break + + if target_sock: + # Send the direct message to the socket-based client + target_sock.send(f"[{timestamp}] DM from {sender_username}: {message}\n".encode('utf-8')) + return jsonify({"status": "Direct message sent to socket-based client"}), 200 + elif target_websocket_sid: + # Send the direct message to the WebSocket client + socketio.emit('message', { + 'username': sender_username, + 'message': message, + 'timestamp': timestamp, + 'chat_room': 'Direct Message' + }, to=target_websocket_sid) + return jsonify({"status": "Direct message sent to WebSocket client"}), 200 + else: + # Target user not found + return jsonify({"error": "Target user not found"}), 404 + +# WebSocket event handlers +@socketio.on('connect') +def handle_connect(): + print("WebSocket client connected") + # Assign a unique ID to the WebSocket client + user_id = str(uuid.uuid4()) + client_info[request.sid] = {"user_id": user_id, "username": None, "is_websocket": True} + print(f"WebSocket client registered with ID: {user_id}") + +@socketio.on('disconnect') +def handle_disconnect(): + print("WebSocket client disconnected") + +@socketio.on('join') +def handle_join(data): + print(f"Received join event: {data}") + username = data.get('username') + chat_room = data.get('chat_room', 'default') + + if not username: + emit('error', {'message': 'Username is required'}) + return + + # Update client info for WebSocket clients + client_info[request.sid]["username"] = username + client_info[request.sid]["chat_room"] = chat_room + + join_room(chat_room) + emit('message', {'username': 'Server', 'message': f"{username} has joined the chat room '{chat_room}'."}, room=chat_room) + +@socketio.on('leave') +def handle_leave(data): + username = data.get('username') + chat_room = data.get('chat_room', 'default') + leave_room(chat_room) + emit('message', {'message': f"{username} has left the chat room '{chat_room}'."}, room=chat_room) + +@socketio.on('send_message') +def handle_send_message(data): + print(f"Received send_message event: {data}") + username = data.get('username') + chat_room = data.get('chat_room', 'default') + message = data.get('message') + + if not username or not message: + emit('error', {'message': 'Username and message are required'}) + return + + if message.strip().startswith("%"): + handle_command(username, chat_room, message) + else: + handle_message(username, chat_room, message) + +# Chat server logic +def chat_server(): + global quitFlag, server_socket, socket_list, client_info, chat_rooms, client_states, uuid_dict + + # Create and configure the server socket server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # set the socket options, SO_REUSEADDR allows to reuse the same port - # SO_KEEPALIVE enables the socket to send keepalive messages server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) - # bind the socket to the address and port server_socket.bind(('', CHATPORT)) - # listen for incoming connections server_socket.listen(MAX_CONNECT_QUEUE) - # set the server socket to non-blocking server_socket.setblocking(False) - # 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 = {} - # let the user know the server is listening on port CHATPORT. - print(f"Chatserver listening on port: {CHATPORT}") + socket_list.append(server_socket) + print(f"Chat server listening on port {CHATPORT}") - # infinite loop for the program while quitFlag: - # use the select function to monitor the sockets read_sockets, _, _ = select.select(socket_list, [], []) - # loop through the sockets for sock in read_sockets: - # if the socket is the server socket, accept the connection if sock == server_socket: + # Accept new client connections client_socket, client_address = server_socket.accept() client_socket.setblocking(False) socket_list.append(client_socket) - 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: ") + print(f"Accepted connection from {client_address}") + client_socket.send(b"Welcome to the chat server!\n") + client_socket.send(b"Type in a username: \n") client_states[client_socket] = "WAITING_FOR_USERNAME" - # otherwise the socket is a client socket. + user_id = get_client_uuid(client_socket, uuid_dict) + client_info[client_socket] = {"user_id": user_id} else: - # try to receive the message from the client + # Handle client messages try: message = sock.recv(BUFFER_SIZE).decode('utf-8') 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.") - client_states[sock] = "NORMAL" + username = message.strip() + if username == "admin": + 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 == "adminpass": + 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() - sock.send(b"\nUsername changed successfully.") - print(f"\nClient switched username to {usernames[sock]}.") + client_info[sock]["username"] = message.strip() + sock.send(b"\nUsername changed successfully.\n") + 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] chat_rooms[sock] = message.strip() - sock.send(f"\nSwitched to chatroom '{chat_rooms[sock]}.'".encode('utf-8')) + sock.send(f"\nSwitched to chatroom '{chat_rooms[sock]}.'\n".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.") @@ -79,37 +422,22 @@ 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.") - elif message.strip() == "%?": - print("\nClient requested help.") - sock.send(b"Help: (%?)\n") - sock.send(b"Send messages like normal. Use enter to send a message.\n") - sock.send(b"Available Commands:\n") - sock.send(b"\t%? : Show this help prompt.\n") - 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") - elif message.strip() == "%*": - sock.send(b"\nEnter chatroom name: ") - client_states[sock] = "WAITING_FOR_NEW_CHATROOM" - elif message.strip() == "%$": - sock.send(b"\nEnter new username: ") - client_states[sock] = "WAITING_FOR_NEW_USERNAME" else: - username = usernames[sock] - chat_room = chat_rooms[sock] - send_message = f"{username} >> {message}".encode('utf-8') - for client_sock in socket_list: - if client_sock != server_socket and chat_rooms.get(client_sock) == chat_room: - client_sock.send(send_message) + if message.strip().startswith("%"): + handle_command(client_info[sock]["username"], chat_rooms[sock], message, sock) + else: + username = client_info[sock]["username"] + chat_room = chat_rooms[sock] + handle_message(username, chat_room, message) 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 +450,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,7 +461,14 @@ def main(): del client_states[sock] print("\nClient has closed connection.") + # Save data before shutting down + save_data(client_info, chat_rooms, client_states) server_socket.close() if __name__ == "__main__": - main() \ No newline at end of file + # Start the chat server in a separate thread + server_thread = threading.Thread(target=chat_server, daemon=True) + server_thread.start() + + # Start the Flask REST API and WebSocket server + socketio.run(app, host='0.0.0.0', port=5000) \ No newline at end of file diff --git a/chatserver/templates/index.html b/chatserver/templates/index.html new file mode 100644 index 0000000..4018ba3 --- /dev/null +++ b/chatserver/templates/index.html @@ -0,0 +1,376 @@ + + + + + + Web Chat Client + + + + + + + +
+ Username: | Chat Room: | Server: +
+ + + + + +
+
+ + +
+ + +
+ + +
+ + + + \ No newline at end of file diff --git a/chatserver/webclient.py b/chatserver/webclient.py new file mode 100644 index 0000000..2a1856c --- /dev/null +++ b/chatserver/webclient.py @@ -0,0 +1,10 @@ +from flask import Flask, render_template + +app = Flask(__name__) + +@app.route('/') +def index(): + return render_template('index.html') # Serve the HTML client + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=8080) # Run the web client on port 8080 \ 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/networksim/CS460Homework4_CBvCB_graph.png b/networksim/CS460Homework4_CBvCB_graph.png deleted file mode 100644 index 0005c3a..0000000 Binary files a/networksim/CS460Homework4_CBvCB_graph.png and /dev/null differ diff --git a/networksim/CS460Homework4_Cubic_graph.png b/networksim/CS460Homework4_Cubic_graph.png deleted file mode 100644 index b98c5b3..0000000 Binary files a/networksim/CS460Homework4_Cubic_graph.png and /dev/null differ diff --git a/networksim/CS460Homework4_NRvCB_graph.png b/networksim/CS460Homework4_NRvCB_graph.png deleted file mode 100644 index b325280..0000000 Binary files a/networksim/CS460Homework4_NRvCB_graph.png and /dev/null differ diff --git a/networksim/CS460Homework4_NRvNR_graph.png b/networksim/CS460Homework4_NRvNR_graph.png deleted file mode 100644 index ba38290..0000000 Binary files a/networksim/CS460Homework4_NRvNR_graph.png and /dev/null differ diff --git a/networksim/CS460Homework4_NewReno_graph.png b/networksim/CS460Homework4_NewReno_graph.png deleted file mode 100644 index 9c251a6..0000000 Binary files a/networksim/CS460Homework4_NewReno_graph.png and /dev/null differ diff --git a/networksim/CS460Homework4_graph.png b/networksim/CS460Homework4_graph.png deleted file mode 100644 index 81b2168..0000000 Binary files a/networksim/CS460Homework4_graph.png and /dev/null differ diff --git a/networksim/CS460Homework4_graph_5p.png b/networksim/CS460Homework4_graph_5p.png deleted file mode 100644 index 8a24d71..0000000 Binary files a/networksim/CS460Homework4_graph_5p.png and /dev/null differ diff --git a/networksim/README.md b/networksim/README.md deleted file mode 100644 index c81d21b..0000000 --- a/networksim/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Adapted tcp-variants-comparison file -The `tcp-variants-comparison.cc` file has been adapted to allow for multiple TCP variants to be run in one simulation. -In order to change the variants ran, find line 510-524. This is where the magic of which variants to run is. -The code relies on multiple flows to also be ran, which can be found using the number of cases - 1. If you want to test the file, you'll want to replace the file with this modified version, which must then be rebuilt. You should be able to use `./ns3 build tcp-variants-comparison` (or `./ns3 build examples/tcp-variants-comparison` might work better) although this hasn't been tested, and the best method is to use ccache to cache the build before making the modification. - -## How to find the cwnd of multiple TCP variants (how to run) -The command `./ns3 run tcp-variants-comparison --command-template="%s --tracing=1 --prefix_name=CS460Homework4_TCPCmp --num_flows=3 --access_delay=50ms"` can be used to find the comparisons between TCP variants. You'll want the number of flows to equal the amount of cases in the switch statement. \ No newline at end of file diff --git a/networksim/tcp-variants-comparison.cc b/networksim/tcp-variants-comparison.cc deleted file mode 100644 index 09d3142..0000000 --- a/networksim/tcp-variants-comparison.cc +++ /dev/null @@ -1,624 +0,0 @@ -/* - * Copyright (c) 2013 ResiliNets, ITTC, University of Kansas - * - * SPDX-License-Identifier: GPL-2.0-only - * - * Authors: Justin P. Rohrer, Truc Anh N. Nguyen , Siddharth Gangadhar - * - * - * James P.G. Sterbenz , director - * ResiliNets Research Group https://resilinets.org/ - * Information and Telecommunication Technology Center (ITTC) - * and Department of Electrical Engineering and Computer Science - * The University of Kansas Lawrence, KS USA. - * - * Work supported in part by NSF FIND (Future Internet Design) Program - * under grant CNS-0626918 (Postmodern Internet Architecture), - * NSF grant CNS-1050226 (Multilayer Network Resilience Analysis and Experimentation on GENI), - * US Department of Defense (DoD), and ITTC at The University of Kansas. - * - * "TCP Westwood(+) Protocol Implementation in ns-3" - * Siddharth Gangadhar, Trúc Anh Ngọc Nguyễn , Greeshma Umapathi, and James P.G. Sterbenz, - * ICST SIMUTools Workshop on ns-3 (WNS3), Cannes, France, March 2013 - */ - -#include "ns3/applications-module.h" -#include "ns3/core-module.h" -#include "ns3/enum.h" -#include "ns3/error-model.h" -#include "ns3/event-id.h" -#include "ns3/flow-monitor-helper.h" -#include "ns3/internet-module.h" -#include "ns3/ipv4-global-routing-helper.h" -#include "ns3/network-module.h" -#include "ns3/point-to-point-module.h" -#include "ns3/tcp-header.h" -#include "ns3/traffic-control-module.h" -#include "ns3/udp-header.h" - -#include -#include -#include - -using namespace ns3; - -NS_LOG_COMPONENT_DEFINE("TcpVariantsComparison"); - -static std::map firstCwnd; //!< First congestion window. -static std::map firstSshThr; //!< First SlowStart threshold. -static std::map firstRtt; //!< First RTT. -static std::map firstRto; //!< First RTO. -static std::map> cWndStream; //!< Congstion window output stream. -static std::map> - ssThreshStream; //!< SlowStart threshold output stream. -static std::map> rttStream; //!< RTT output stream. -static std::map> rtoStream; //!< RTO output stream. -static std::map> nextTxStream; //!< Next TX output stream. -static std::map> nextRxStream; //!< Next RX output stream. -static std::map> inFlightStream; //!< In flight output stream. -static std::map cWndValue; //!< congestion window value. -static std::map ssThreshValue; //!< SlowStart threshold value. - -/** - * Get the Node Id From Context. - * - * @param context The context. - * @return the node ID. - */ -static uint32_t -GetNodeIdFromContext(std::string context) -{ - const std::size_t n1 = context.find_first_of('/', 1); - const std::size_t n2 = context.find_first_of('/', n1 + 1); - return std::stoul(context.substr(n1 + 1, n2 - n1 - 1)); -} - -/** - * Congestion window tracer. - * - * @param context The context. - * @param oldval Old value. - * @param newval New value. - */ -static void -CwndTracer(std::string context, uint32_t oldval, uint32_t newval) -{ - uint32_t nodeId = GetNodeIdFromContext(context); - - if (firstCwnd[nodeId]) - { - *cWndStream[nodeId]->GetStream() << "0.0 " << oldval << std::endl; - firstCwnd[nodeId] = false; - } - *cWndStream[nodeId]->GetStream() << Simulator::Now().GetSeconds() << " " << newval << std::endl; - cWndValue[nodeId] = newval; - - if (!firstSshThr[nodeId]) - { - *ssThreshStream[nodeId]->GetStream() - << Simulator::Now().GetSeconds() << " " << ssThreshValue[nodeId] << std::endl; - } -} - -/** - * Slow start threshold tracer. - * - * @param context The context. - * @param oldval Old value. - * @param newval New value. - */ -static void -SsThreshTracer(std::string context, uint32_t oldval, uint32_t newval) -{ - uint32_t nodeId = GetNodeIdFromContext(context); - - if (firstSshThr[nodeId]) - { - *ssThreshStream[nodeId]->GetStream() << "0.0 " << oldval << std::endl; - firstSshThr[nodeId] = false; - } - *ssThreshStream[nodeId]->GetStream() - << Simulator::Now().GetSeconds() << " " << newval << std::endl; - ssThreshValue[nodeId] = newval; - - if (!firstCwnd[nodeId]) - { - *cWndStream[nodeId]->GetStream() - << Simulator::Now().GetSeconds() << " " << cWndValue[nodeId] << std::endl; - } -} - -/** - * RTT tracer. - * - * @param context The context. - * @param oldval Old value. - * @param newval New value. - */ -static void -RttTracer(std::string context, Time oldval, Time newval) -{ - uint32_t nodeId = GetNodeIdFromContext(context); - - if (firstRtt[nodeId]) - { - *rttStream[nodeId]->GetStream() << "0.0 " << oldval.GetSeconds() << std::endl; - firstRtt[nodeId] = false; - } - *rttStream[nodeId]->GetStream() - << Simulator::Now().GetSeconds() << " " << newval.GetSeconds() << std::endl; -} - -/** - * RTO tracer. - * - * @param context The context. - * @param oldval Old value. - * @param newval New value. - */ -static void -RtoTracer(std::string context, Time oldval, Time newval) -{ - uint32_t nodeId = GetNodeIdFromContext(context); - - if (firstRto[nodeId]) - { - *rtoStream[nodeId]->GetStream() << "0.0 " << oldval.GetSeconds() << std::endl; - firstRto[nodeId] = false; - } - *rtoStream[nodeId]->GetStream() - << Simulator::Now().GetSeconds() << " " << newval.GetSeconds() << std::endl; -} - -/** - * Next TX tracer. - * - * @param context The context. - * @param old Old sequence number. - * @param nextTx Next sequence number. - */ -static void -NextTxTracer(std::string context, SequenceNumber32 old [[maybe_unused]], SequenceNumber32 nextTx) -{ - uint32_t nodeId = GetNodeIdFromContext(context); - - *nextTxStream[nodeId]->GetStream() - << Simulator::Now().GetSeconds() << " " << nextTx << std::endl; -} - -/** - * In-flight tracer. - * - * @param context The context. - * @param old Old value. - * @param inFlight In flight value. - */ -static void -InFlightTracer(std::string context, uint32_t old [[maybe_unused]], uint32_t inFlight) -{ - uint32_t nodeId = GetNodeIdFromContext(context); - - *inFlightStream[nodeId]->GetStream() - << Simulator::Now().GetSeconds() << " " << inFlight << std::endl; -} - -/** - * Next RX tracer. - * - * @param context The context. - * @param old Old sequence number. - * @param nextRx Next sequence number. - */ -static void -NextRxTracer(std::string context, SequenceNumber32 old [[maybe_unused]], SequenceNumber32 nextRx) -{ - uint32_t nodeId = GetNodeIdFromContext(context); - - *nextRxStream[nodeId]->GetStream() - << Simulator::Now().GetSeconds() << " " << nextRx << std::endl; -} - -/** - * Congestion window trace connection. - * - * @param cwnd_tr_file_name Congestion window trace file name. - * @param nodeId Node ID. - */ -static void -TraceCwnd(std::string cwnd_tr_file_name, uint32_t nodeId) -{ - AsciiTraceHelper ascii; - cWndStream[nodeId] = ascii.CreateFileStream(cwnd_tr_file_name); - Config::Connect("/NodeList/" + std::to_string(nodeId) + - "/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow", - MakeCallback(&CwndTracer)); -} - -/** - * Slow start threshold trace connection. - * - * @param ssthresh_tr_file_name Slow start threshold trace file name. - * @param nodeId Node ID. - */ -static void -TraceSsThresh(std::string ssthresh_tr_file_name, uint32_t nodeId) -{ - AsciiTraceHelper ascii; - ssThreshStream[nodeId] = ascii.CreateFileStream(ssthresh_tr_file_name); - Config::Connect("/NodeList/" + std::to_string(nodeId) + - "/$ns3::TcpL4Protocol/SocketList/0/SlowStartThreshold", - MakeCallback(&SsThreshTracer)); -} - -/** - * RTT trace connection. - * - * @param rtt_tr_file_name RTT trace file name. - * @param nodeId Node ID. - */ -static void -TraceRtt(std::string rtt_tr_file_name, uint32_t nodeId) -{ - AsciiTraceHelper ascii; - rttStream[nodeId] = ascii.CreateFileStream(rtt_tr_file_name); - Config::Connect("/NodeList/" + std::to_string(nodeId) + "/$ns3::TcpL4Protocol/SocketList/0/RTT", - MakeCallback(&RttTracer)); -} - -/** - * RTO trace connection. - * - * @param rto_tr_file_name RTO trace file name. - * @param nodeId Node ID. - */ -static void -TraceRto(std::string rto_tr_file_name, uint32_t nodeId) -{ - AsciiTraceHelper ascii; - rtoStream[nodeId] = ascii.CreateFileStream(rto_tr_file_name); - Config::Connect("/NodeList/" + std::to_string(nodeId) + "/$ns3::TcpL4Protocol/SocketList/0/RTO", - MakeCallback(&RtoTracer)); -} - -/** - * Next TX trace connection. - * - * @param next_tx_seq_file_name Next TX trace file name. - * @param nodeId Node ID. - */ -static void -TraceNextTx(std::string& next_tx_seq_file_name, uint32_t nodeId) -{ - AsciiTraceHelper ascii; - nextTxStream[nodeId] = ascii.CreateFileStream(next_tx_seq_file_name); - Config::Connect("/NodeList/" + std::to_string(nodeId) + - "/$ns3::TcpL4Protocol/SocketList/0/NextTxSequence", - MakeCallback(&NextTxTracer)); -} - -/** - * In flight trace connection. - * - * @param in_flight_file_name In flight trace file name. - * @param nodeId Node ID. - */ -static void -TraceInFlight(std::string& in_flight_file_name, uint32_t nodeId) -{ - AsciiTraceHelper ascii; - inFlightStream[nodeId] = ascii.CreateFileStream(in_flight_file_name); - Config::Connect("/NodeList/" + std::to_string(nodeId) + - "/$ns3::TcpL4Protocol/SocketList/0/BytesInFlight", - MakeCallback(&InFlightTracer)); -} - -/** - * Next RX trace connection. - * - * @param next_rx_seq_file_name Next RX trace file name. - * @param nodeId Node ID. - */ -static void -TraceNextRx(std::string& next_rx_seq_file_name, uint32_t nodeId) -{ - AsciiTraceHelper ascii; - nextRxStream[nodeId] = ascii.CreateFileStream(next_rx_seq_file_name); - Config::Connect("/NodeList/" + std::to_string(nodeId) + - "/$ns3::TcpL4Protocol/SocketList/1/RxBuffer/NextRxSequence", - MakeCallback(&NextRxTracer)); -} - -int -main(int argc, char* argv[]) -{ - std::string transport_prot = "TcpWestwoodPlus"; - double error_p = 0.0; - std::string bandwidth = "2Mbps"; - std::string delay = "0.01ms"; - std::string access_bandwidth = "10Mbps"; - std::string access_delay = "45ms"; - bool tracing = false; - std::string prefix_file_name = "TcpVariantsComparison"; - uint64_t data_mbytes = 0; - uint32_t mtu_bytes = 400; - uint16_t num_flows = 1; - double duration = 100.0; - uint32_t run = 0; - bool flow_monitor = false; - bool pcap = false; - bool sack = true; - std::string queue_disc_type = "ns3::PfifoFastQueueDisc"; - std::string recovery = "ns3::TcpClassicRecovery"; - - CommandLine cmd(__FILE__); - cmd.AddValue("transport_prot", - "Transport protocol to use: TcpNewReno, TcpLinuxReno, " - "TcpHybla, TcpHighSpeed, TcpHtcp, TcpVegas, TcpScalable, TcpVeno, " - "TcpBic, TcpYeah, TcpIllinois, TcpWestwoodPlus, TcpLedbat, " - "TcpLp, TcpDctcp, TcpCubic, TcpBbr", - transport_prot); - cmd.AddValue("error_p", "Packet error rate", error_p); - cmd.AddValue("bandwidth", "Bottleneck bandwidth", bandwidth); - cmd.AddValue("delay", "Bottleneck delay", delay); - cmd.AddValue("access_bandwidth", "Access link bandwidth", access_bandwidth); - cmd.AddValue("access_delay", "Access link delay", access_delay); - cmd.AddValue("tracing", "Flag to enable/disable tracing", tracing); - cmd.AddValue("prefix_name", "Prefix of output trace file", prefix_file_name); - cmd.AddValue("data", "Number of Megabytes of data to transmit", data_mbytes); - cmd.AddValue("mtu", "Size of IP packets to send in bytes", mtu_bytes); - cmd.AddValue("num_flows", "Number of flows", num_flows); - cmd.AddValue("duration", "Time to allow flows to run in seconds", duration); - cmd.AddValue("run", "Run index (for setting repeatable seeds)", run); - cmd.AddValue("flow_monitor", "Enable flow monitor", flow_monitor); - cmd.AddValue("pcap_tracing", "Enable or disable PCAP tracing", pcap); - cmd.AddValue("queue_disc_type", - "Queue disc type for gateway (e.g. ns3::CoDelQueueDisc)", - queue_disc_type); - cmd.AddValue("sack", "Enable or disable SACK option", sack); - cmd.AddValue("recovery", "Recovery algorithm type to use (e.g., ns3::TcpPrrRecovery", recovery); - cmd.Parse(argc, argv); - - transport_prot = std::string("ns3::") + transport_prot; - - SeedManager::SetSeed(1); - SeedManager::SetRun(run); - - // User may find it convenient to enable logging - // LogComponentEnable("TcpVariantsComparison", LOG_LEVEL_ALL); - // LogComponentEnable("BulkSendApplication", LOG_LEVEL_INFO); - // LogComponentEnable("PfifoFastQueueDisc", LOG_LEVEL_ALL); - - // Calculate the ADU size - Header* temp_header = new Ipv4Header(); - uint32_t ip_header = temp_header->GetSerializedSize(); - NS_LOG_LOGIC("IP Header size is: " << ip_header); - delete temp_header; - temp_header = new TcpHeader(); - uint32_t tcp_header = temp_header->GetSerializedSize(); - NS_LOG_LOGIC("TCP Header size is: " << tcp_header); - delete temp_header; - uint32_t tcp_adu_size = mtu_bytes - 20 - (ip_header + tcp_header); - NS_LOG_LOGIC("TCP ADU size is: " << tcp_adu_size); - - // Set the simulation start and stop time - double start_time = 0.1; - double stop_time = start_time + duration; - - // 2 MB of TCP buffer - Config::SetDefault("ns3::TcpSocket::RcvBufSize", UintegerValue(1 << 21)); - Config::SetDefault("ns3::TcpSocket::SndBufSize", UintegerValue(1 << 21)); - Config::SetDefault("ns3::TcpSocketBase::Sack", BooleanValue(sack)); - - Config::SetDefault("ns3::TcpL4Protocol::RecoveryType", - TypeIdValue(TypeId::LookupByName(recovery))); - // Select TCP variant - TypeId tcpTid; - NS_ABORT_MSG_UNLESS(TypeId::LookupByNameFailSafe(transport_prot, &tcpTid), - "TypeId " << transport_prot << " not found"); - Config::SetDefault("ns3::TcpL4Protocol::SocketType", - TypeIdValue(TypeId::LookupByName(transport_prot))); - - // Create gateways, sources, and sinks - NodeContainer gateways; - gateways.Create(1); - NodeContainer sources; - sources.Create(num_flows); - NodeContainer sinks; - sinks.Create(num_flows); - - // Configure the error model - // Here we use RateErrorModel with packet error rate - Ptr uv = CreateObject(); - uv->SetStream(50); - RateErrorModel error_model; - error_model.SetRandomVariable(uv); - error_model.SetUnit(RateErrorModel::ERROR_UNIT_PACKET); - error_model.SetRate(error_p); - - PointToPointHelper UnReLink; - UnReLink.SetDeviceAttribute("DataRate", StringValue(bandwidth)); - UnReLink.SetChannelAttribute("Delay", StringValue(delay)); - UnReLink.SetDeviceAttribute("ReceiveErrorModel", PointerValue(&error_model)); - - InternetStackHelper stack; - stack.InstallAll(); - - TrafficControlHelper tchPfifo; - tchPfifo.SetRootQueueDisc("ns3::PfifoFastQueueDisc"); - - TrafficControlHelper tchCoDel; - tchCoDel.SetRootQueueDisc("ns3::CoDelQueueDisc"); - - Ipv4AddressHelper address; - address.SetBase("10.0.0.0", "255.255.255.0"); - - // Configure the sources and sinks net devices - // and the channels between the sources/sinks and the gateways - PointToPointHelper LocalLink; - LocalLink.SetDeviceAttribute("DataRate", StringValue(access_bandwidth)); - LocalLink.SetChannelAttribute("Delay", StringValue(access_delay)); - - Ipv4InterfaceContainer sink_interfaces; - - DataRate access_b(access_bandwidth); - DataRate bottle_b(bandwidth); - Time access_d(access_delay); - Time bottle_d(delay); - - uint32_t size = static_cast((std::min(access_b, bottle_b).GetBitRate() / 8) * - ((access_d + bottle_d) * 2).GetSeconds()); - - Config::SetDefault("ns3::PfifoFastQueueDisc::MaxSize", - QueueSizeValue(QueueSize(QueueSizeUnit::PACKETS, size / mtu_bytes))); - Config::SetDefault("ns3::CoDelQueueDisc::MaxSize", - QueueSizeValue(QueueSize(QueueSizeUnit::BYTES, size))); - - for (uint32_t i = 0; i < num_flows; i++) - { - NetDeviceContainer devices; - devices = LocalLink.Install(sources.Get(i), gateways.Get(0)); - tchPfifo.Install(devices); - address.NewNetwork(); - Ipv4InterfaceContainer interfaces = address.Assign(devices); - - devices = UnReLink.Install(gateways.Get(0), sinks.Get(i)); - if (queue_disc_type == "ns3::PfifoFastQueueDisc") - { - tchPfifo.Install(devices); - } - else if (queue_disc_type == "ns3::CoDelQueueDisc") - { - tchCoDel.Install(devices); - } - else - { - NS_FATAL_ERROR("Queue not recognized. Allowed values are ns3::CoDelQueueDisc or " - "ns3::PfifoFastQueueDisc"); - } - address.NewNetwork(); - interfaces = address.Assign(devices); - sink_interfaces.Add(interfaces.Get(1)); - } - - NS_LOG_INFO("Initialize Global Routing."); - Ipv4GlobalRoutingHelper::PopulateRoutingTables(); - - uint16_t port = 50000; - Address sinkLocalAddress(InetSocketAddress(Ipv4Address::GetAny(), port)); - PacketSinkHelper sinkHelper("ns3::TcpSocketFactory", sinkLocalAddress); - - for (uint32_t i = 0; i < sources.GetN(); i++) - { - // Assign a different TCP variant to each flow - std::string tcpVariant; - switch (i % 2) // Example: Rotate between two TCP variants - { - case 0: - tcpVariant = "ns3::TcpNewReno"; - break; - case 1: - tcpVariant = "ns3::TcpCubic"; - break; - default: - tcpVariant = "ns3::TcpNewReno"; - } - - // Set the TCP variant for this flow - Config::Set("/NodeList/" + std::to_string(sources.Get(i)->GetId()) + - "/$ns3::TcpL4Protocol/SocketType", - StringValue(tcpVariant)); - - // Configure the BulkSend application - Address remoteAddress(InetSocketAddress(sink_interfaces.GetAddress(i, 0), port)); - BulkSendHelper ftp("ns3::TcpSocketFactory", remoteAddress); - ftp.SetAttribute("SendSize", UintegerValue(tcp_adu_size)); - ftp.SetAttribute("MaxBytes", UintegerValue(data_mbytes * 1000000)); - - ApplicationContainer sourceApp = ftp.Install(sources.Get(i)); - sourceApp.Start(Seconds(start_time * i)); - sourceApp.Stop(Seconds(stop_time - 3)); - - // Configure the PacketSink application - sinkHelper.SetAttribute("Protocol", TypeIdValue(TcpSocketFactory::GetTypeId())); - ApplicationContainer sinkApp = sinkHelper.Install(sinks.Get(i)); - sinkApp.Start(Seconds(start_time * i)); - sinkApp.Stop(Seconds(stop_time)); - } - - // Set up tracing if enabled - if (tracing) - { - std::ofstream ascii; - Ptr ascii_wrap; - ascii.open(prefix_file_name + "-ascii"); - ascii_wrap = new OutputStreamWrapper(prefix_file_name + "-ascii", std::ios::out); - stack.EnableAsciiIpv4All(ascii_wrap); - - for (uint16_t index = 0; index < num_flows; index++) - { - std::string flowString; - if (num_flows > 1) - { - flowString = "-flow" + std::to_string(index); - } - - firstCwnd[index + 1] = true; - firstSshThr[index + 1] = true; - firstRtt[index + 1] = true; - firstRto[index + 1] = true; - - Simulator::Schedule(Seconds(start_time * index + 0.00001), - &TraceCwnd, - prefix_file_name + flowString + "-cwnd.data", - index + 1); - Simulator::Schedule(Seconds(start_time * index + 0.00001), - &TraceSsThresh, - prefix_file_name + flowString + "-ssth.data", - index + 1); - Simulator::Schedule(Seconds(start_time * index + 0.00001), - &TraceRtt, - prefix_file_name + flowString + "-rtt.data", - index + 1); - Simulator::Schedule(Seconds(start_time * index + 0.00001), - &TraceRto, - prefix_file_name + flowString + "-rto.data", - index + 1); - Simulator::Schedule(Seconds(start_time * index + 0.00001), - &TraceNextTx, - prefix_file_name + flowString + "-next-tx.data", - index + 1); - Simulator::Schedule(Seconds(start_time * index + 0.00001), - &TraceInFlight, - prefix_file_name + flowString + "-inflight.data", - index + 1); - Simulator::Schedule(Seconds(start_time * index + 0.1), - &TraceNextRx, - prefix_file_name + flowString + "-next-rx.data", - num_flows + index + 1); - } - } - - if (pcap) - { - UnReLink.EnablePcapAll(prefix_file_name, true); - LocalLink.EnablePcapAll(prefix_file_name, true); - } - - // Flow monitor - FlowMonitorHelper flowHelper; - if (flow_monitor) - { - flowHelper.InstallAll(); - } - - Simulator::Stop(Seconds(stop_time)); - Simulator::Run(); - - if (flow_monitor) - { - flowHelper.SerializeToXmlFile(prefix_file_name + ".flowmonitor", true, true); - } - - Simulator::Destroy(); - return 0; -} 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 6c48bde..0000000 Binary files a/timeserver/demo.pcap and /dev/null differ 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