diff --git a/chatserver/client.py b/chatserver/client.py index 00e4254..9ad3c98 100644 --- a/chatserver/client.py +++ b/chatserver/client.py @@ -1,4 +1,3 @@ -import os import socket import sys import select @@ -16,25 +15,13 @@ def main(): SERVER_HOST = sys.argv[1] SERVER_PORT = int(sys.argv[2]) - # Prompt user for a local directory path for file operations - while True: - local_path = input("Enter a local directory path for sending/receiving files: ").strip() - if os.path.isdir(local_path): - print(f"Using directory: {local_path}") - break - else: - print("Invalid directory. Please enter a valid path.") - # 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}") - - # Send the local path to the server - client_socket.send(f"SET_PATH {local_path}".encode('utf-8')) - + while True: # use select to wait for either server messages or user input read_sockets, _, _ = select.select([client_socket, sys.stdin], [], []) @@ -46,35 +33,8 @@ def main(): if not server_message: print("Disconnected from server") return - if server_message.startswith("FILE_REQUEST"): - # Handle file request - _, sending_username, filename = server_message.split() - file_path = os.path.join(local_path, filename) - if os.path.isfile(file_path): - file_size = os.path.getsize(file_path) - with open(file_path, "rb") as f: - file_bytes = f.read() - filetype = "File" - client_socket.send( - f"@{sending_username}@CLIENT -f {filename} {filetype} {file_size} {file_bytes.decode('latin1')} ENDFILE".encode('utf-8') - ) - else: - client_socket.send( - f"@{sending_username}@CLIENT -f {filename} NOTFOUND".encode('utf-8') - ) - elif server_message.startswith("FILE_TRANSFER"): - # Handle file transfer - _, sending_username, filename, filetype, filelength = server_message.split() - file_bytes = client_socket.recv(int(filelength)) - end_marker = client_socket.recv(BUFFER_SIZE).decode('utf-8') - if end_marker == "ENDFILE": - file_path = os.path.join(local_path, filename) - with open(file_path, "wb") as f: - f.write(file_bytes) - print(f"Received file '{filename}' from {sending_username}.") - else: - print(f"\n{server_message}") - print("> ", end="", flush=True) + print(f"\n{server_message}") + print("> ", end="", flush=True) else: # prompt for user input client_message = input() diff --git a/chatserver/server.py b/chatserver/server.py index 59ffe71..ee1d18b 100644 --- a/chatserver/server.py +++ b/chatserver/server.py @@ -1,4 +1,3 @@ -import os import socket import select @@ -6,18 +5,6 @@ CHATPORT = 8181 BUFFER_SIZE = 1024 MAX_CONNECT_QUEUE = 10 -def format_file_info(file_path): - """Helper function to format file information.""" - file_size = os.path.getsize(file_path) - if file_size < 1000: - size_str = f"{file_size} B" - elif file_size < 1000000: - size_str = f"{file_size / 1000:.2f} KB" - else: - size_str = f"{file_size / 1000000:.2f} MB" - file_type = "Directory" if os.path.isdir(file_path) else "File" - return f"{os.path.basename(file_path)} - {file_type} - {size_str}" - def main(): # set the flag variable to true for infinite loop quitFlag = True @@ -44,8 +31,6 @@ def main(): # 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 = {} - # Add a dictionary to store user-specific paths - user_paths = {} # let the user know the server is listening on port CHATPORT. print(f"Chatserver listening on port: {CHATPORT}") @@ -65,7 +50,6 @@ def main(): client_socket.send(b"\nUse '%!' to exit, or '%?' for help.\n") client_socket.send(b"\nType in a username: ") client_states[client_socket] = "WAITING_FOR_USERNAME" - user_paths[client_socket] = None # Initialize path for new client # otherwise the socket is a client socket. else: # try to receive the message from the client @@ -78,7 +62,6 @@ def main(): chat_rooms[sock] = "default" sock.send(b"\nUsername accepted. You are now in the 'default' chatroom.") client_states[sock] = "NORMAL" - user_paths[sock] = None # Initialize path for the user elif state == "WAITING_FOR_NEW_USERNAME": usernames[sock] = message.strip() sock.send(b"\nUsername changed successfully.") @@ -92,82 +75,8 @@ def main(): if old_chatroom != "default" and old_chatroom not in chat_rooms.values(): print(f"\nChatroom '{old_chatroom}' is empty and removed.") client_states[sock] = "NORMAL" - elif state == "NORMAL": - if message.startswith("@") and "-ls" in message: - # Parse the message format: @RECEIVING_USERNAME@SENDING_USERNAME -ls - parts = message.split() - if len(parts) >= 2 and "-ls" in parts[1]: - usernames_part = parts[0] - if usernames_part.count("@") == 2: - receiving_username, sending_username = usernames_part[1:].split("@") - # Find the receiving user's socket - receiving_sock = next( - (s for s, u in usernames.items() if u == receiving_username), None - ) - sending_sock = next( - (s for s, u in usernames.items() if u == sending_username), None - ) - if receiving_sock and sending_sock: - user_path = user_paths.get(receiving_sock) - if user_path and os.path.isdir(user_path): - # List files in the receiving user's directory - files = os.listdir(user_path) - if files: - file_list = "\n".join( - [format_file_info(os.path.join(user_path, f)) for f in files] - ) - sending_sock.send( - f"Files in {receiving_username}'s directory:\n{file_list}".encode('utf-8') - ) - else: - sending_sock.send( - f"No files in {receiving_username}'s directory.".encode('utf-8') - ) - else: - sending_sock.send( - f"Error: {receiving_username} has no valid directory set.".encode('utf-8') - ) - else: - sock.send(b"Error: One or both users not found.") - else: - sock.send(b"Error: Invalid message format.") - elif message.startswith("@") and "-f" in message: - # Parse the message format: @RECEIVING_USERNAME@SENDING_USERNAME -f FILENAME - parts = message.split() - if len(parts) >= 3 and "-f" in parts[1]: - usernames_part = parts[0] - if usernames_part.count("@") == 2: - receiving_username, sending_username = usernames_part[1:].split("@") - # Find the receiving user's socket - receiving_sock = next( - (s for s, u in usernames.items() if u == receiving_username), None - ) - sending_sock = next( - (s for s, u in usernames.items() if u == sending_username), None - ) - if receiving_sock and sending_sock: - if len(parts) == 3: - # File request: @RECEIVING_USERNAME@SENDING_USERNAME -f FILENAME - filename = parts[2] - receiving_sock.send( - f"FILE_REQUEST {sending_username} {filename}".encode('utf-8') - ) - elif len(parts) > 5 and parts[-1] == "ENDFILE": - # File transfer: @RECEIVING_USERNAME@SENDING_USERNAME -f FILENAME FILETYPE FILELENGTH FILEBYTES ENDFILE - filename = parts[2] - filetype = parts[3] - filelength = int(parts[4]) - filebytes = " ".join(parts[5:-1]).encode('utf-8') - receiving_sock.send( - f"FILE_TRANSFER {sending_username} {filename} {filetype} {filelength}".encode('utf-8') - ) - receiving_sock.send(filebytes) - receiving_sock.send(b"ENDFILE") - else: - sock.send(b"Error: One or both users not found.") - else: - sock.send(b"Error: Invalid message format.") - elif message.strip() == "%!": + else: + if message.strip() == "%!": sock.close() socket_list.remove(sock) del usernames[sock] diff --git a/echoserver/Makefile b/echoserver/Makefile new file mode 100644 index 0000000..221b29f --- /dev/null +++ b/echoserver/Makefile @@ -0,0 +1,20 @@ +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 new file mode 100644 index 0000000..fb81333 --- /dev/null +++ b/echoserver/README.md @@ -0,0 +1,10 @@ +# 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 new file mode 100644 index 0000000..73e2bbc --- /dev/null +++ b/echoserver/echoserver.c @@ -0,0 +1,150 @@ +// 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 new file mode 100644 index 0000000..b1ef31c --- /dev/null +++ b/echoserver/echoserver.h @@ -0,0 +1,25 @@ +#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 new file mode 100644 index 0000000..0005c3a Binary files /dev/null and b/networksim/CS460Homework4_CBvCB_graph.png differ diff --git a/networksim/CS460Homework4_Cubic_graph.png b/networksim/CS460Homework4_Cubic_graph.png new file mode 100644 index 0000000..b98c5b3 Binary files /dev/null and b/networksim/CS460Homework4_Cubic_graph.png differ diff --git a/networksim/CS460Homework4_NRvCB_graph.png b/networksim/CS460Homework4_NRvCB_graph.png new file mode 100644 index 0000000..b325280 Binary files /dev/null and b/networksim/CS460Homework4_NRvCB_graph.png differ diff --git a/networksim/CS460Homework4_NRvNR_graph.png b/networksim/CS460Homework4_NRvNR_graph.png new file mode 100644 index 0000000..ba38290 Binary files /dev/null and b/networksim/CS460Homework4_NRvNR_graph.png differ diff --git a/networksim/CS460Homework4_NewReno_graph.png b/networksim/CS460Homework4_NewReno_graph.png new file mode 100644 index 0000000..9c251a6 Binary files /dev/null and b/networksim/CS460Homework4_NewReno_graph.png differ diff --git a/networksim/CS460Homework4_graph.png b/networksim/CS460Homework4_graph.png new file mode 100644 index 0000000..81b2168 Binary files /dev/null and b/networksim/CS460Homework4_graph.png differ diff --git a/networksim/CS460Homework4_graph_5p.png b/networksim/CS460Homework4_graph_5p.png new file mode 100644 index 0000000..8a24d71 Binary files /dev/null and b/networksim/CS460Homework4_graph_5p.png differ diff --git a/networksim/README.md b/networksim/README.md new file mode 100644 index 0000000..c81d21b --- /dev/null +++ b/networksim/README.md @@ -0,0 +1,7 @@ +# 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 new file mode 100644 index 0000000..09d3142 --- /dev/null +++ b/networksim/tcp-variants-comparison.cc @@ -0,0 +1,624 @@ +/* + * 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 new file mode 100644 index 0000000..6f7d8b7 --- /dev/null +++ b/timeserver/Makefile @@ -0,0 +1,20 @@ +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 new file mode 100644 index 0000000..b9d8b6a --- /dev/null +++ b/timeserver/README.md @@ -0,0 +1,16 @@ +# 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 new file mode 100644 index 0000000..6c48bde Binary files /dev/null and b/timeserver/demo.pcap differ diff --git a/timeserver/timeserver.c b/timeserver/timeserver.c new file mode 100644 index 0000000..6eef908 --- /dev/null +++ b/timeserver/timeserver.c @@ -0,0 +1,83 @@ +// 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 new file mode 100644 index 0000000..1a8135b --- /dev/null +++ b/timeserver/timeserver.h @@ -0,0 +1,23 @@ +#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