├── README.md ├── lab1 ├── Lab1.pdf └── Lab1.tar ├── lab2 ├── Lab2.pdf └── Lab2.tar ├── lab3 ├── Lab3.pdf ├── Lab3.tar └── WebServer.py ├── lab4 ├── Lab4.pdf └── tcp-ethereal-trace-1 ├── lab5 └── Lab5.pdf ├── lab6 ├── Lab6.pdf └── Lab6.tar ├── .gitattributes ├── sample_questions ├── LinkLayerSolution.pdf ├── SOL_Homework_P2P.pdf ├── Answers_Transport_1.pdf ├── Datalink_Questions.pdf ├── Security_Questions.pdf ├── Answers_Application_1.pdf ├── Network_-2_Sample_Questions.pdf ├── Sample_Questions_Transport_-1.pdf ├── Sample_Questions_Application_-1.pdf ├── Sample_Questions_Application_-2.pdf └── Transport-2_Network-1_Sample_Questions.pdf └── assignment1 ├── receiver.py └── sender.py /README.md: -------------------------------------------------------------------------------- 1 | # COMP9331 2 | -------------------------------------------------------------------------------- /lab1/Lab1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab1/Lab1.pdf -------------------------------------------------------------------------------- /lab1/Lab1.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab1/Lab1.tar -------------------------------------------------------------------------------- /lab2/Lab2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab2/Lab2.pdf -------------------------------------------------------------------------------- /lab2/Lab2.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab2/Lab2.tar -------------------------------------------------------------------------------- /lab3/Lab3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab3/Lab3.pdf -------------------------------------------------------------------------------- /lab3/Lab3.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab3/Lab3.tar -------------------------------------------------------------------------------- /lab4/Lab4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab4/Lab4.pdf -------------------------------------------------------------------------------- /lab5/Lab5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab5/Lab5.pdf -------------------------------------------------------------------------------- /lab6/Lab6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab6/Lab6.pdf -------------------------------------------------------------------------------- /lab6/Lab6.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab6/Lab6.tar -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /lab4/tcp-ethereal-trace-1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/lab4/tcp-ethereal-trace-1 -------------------------------------------------------------------------------- /sample_questions/LinkLayerSolution.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/LinkLayerSolution.pdf -------------------------------------------------------------------------------- /sample_questions/SOL_Homework_P2P.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/SOL_Homework_P2P.pdf -------------------------------------------------------------------------------- /sample_questions/Answers_Transport_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/Answers_Transport_1.pdf -------------------------------------------------------------------------------- /sample_questions/Datalink_Questions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/Datalink_Questions.pdf -------------------------------------------------------------------------------- /sample_questions/Security_Questions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/Security_Questions.pdf -------------------------------------------------------------------------------- /sample_questions/Answers_Application_1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/Answers_Application_1.pdf -------------------------------------------------------------------------------- /sample_questions/Network_-2_Sample_Questions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/Network_-2_Sample_Questions.pdf -------------------------------------------------------------------------------- /sample_questions/Sample_Questions_Transport_-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/Sample_Questions_Transport_-1.pdf -------------------------------------------------------------------------------- /sample_questions/Sample_Questions_Application_-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/Sample_Questions_Application_-1.pdf -------------------------------------------------------------------------------- /sample_questions/Sample_Questions_Application_-2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/Sample_Questions_Application_-2.pdf -------------------------------------------------------------------------------- /sample_questions/Transport-2_Network-1_Sample_Questions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MW-ZHOU/COMP9331/HEAD/sample_questions/Transport-2_Network-1_Sample_Questions.pdf -------------------------------------------------------------------------------- /lab3/WebServer.py: -------------------------------------------------------------------------------- 1 | from socket import * 2 | import sys 3 | # hostname = gethostbyname(gethostname()) 4 | hostname = 'localhost' 5 | serverPort = int(sys.argv[1]) 6 | 7 | print(f"\nIP address:{hostname}") 8 | serverSocket = socket(AF_INET, SOCK_STREAM) 9 | # create a socket for listening request 10 | serverSocket.bind((hostname, serverPort)) 11 | serverSocket.listen(1) 12 | 13 | while True: 14 | # print("\n\nThe server is Ready...") 15 | # create a socket for TCP connection 16 | connectionSocket, address = serverSocket.accept() 17 | try: 18 | # receive HTTP request from the client and decode the bytes strings. 19 | request = connectionSocket.recv(1024).decode() 20 | # print(f"\nHTTP request message:") 21 | # print(request) 22 | # get the name of the file that the client wants 23 | requested_file = request.split()[1] 24 | # open the file and read its content 25 | file = open(requested_file[1:], 'rb') 26 | content = file.read() 27 | # print(content) 28 | # send a HTTP header line within the socket and encode 29 | connectionSocket.send(b"HTTP/1.1 200 OK\r\n\r\n") 30 | # send the contend of the file requested by the browser 31 | connectionSocket.send(content) 32 | connectionSocket.close() 33 | 34 | except IOError: 35 | connectionSocket.send(b"HTTP/1.1 404 Not Found\r\n\r\n") 36 | connectionSocket.send(b"404 Not Found") 37 | connectionSocket.close() 38 | -------------------------------------------------------------------------------- /assignment1/receiver.py: -------------------------------------------------------------------------------- 1 | # comp9331 assignment written by Maowen Zhou 2 | # Using python 3.7 3 | 4 | from socket import * 5 | import struct 6 | import time 7 | import sys 8 | import pickle 9 | 10 | 11 | # elegantly get the IP address of the host machine 12 | def get_host_IP(): 13 | s = socket(AF_INET, SOCK_DGRAM) 14 | try: 15 | s.connect(('8.8.8.8', 80)) 16 | host_ip = s.getsockname()[0] 17 | finally: 18 | s.close() 19 | return host_ip 20 | 21 | 22 | class Header: 23 | def __init__(self): 24 | self.source_IP = get_host_IP() 25 | self.source_port = 0 26 | self.destination_IP = '' 27 | self.destination_port = 0 28 | self.Sequence_num = 0 29 | self.sender_seq = 0 30 | self.ACK_num = 0 31 | self.recv_ACK = 0 32 | self.SYN = 0 33 | self.FIN = 0 34 | self.FINACK = 0 35 | self.MWS = 0 36 | self.MSS = 0 37 | self.gamma = 0 38 | self.state = 'closed' 39 | self.file_name = '' 40 | self.send_data = b'' 41 | self.checksum = 0 42 | self.recv_data = b'' 43 | self.len_send_data = 0 44 | self.len_recv_data = 0 45 | # for the log file 46 | self.start_time = 0 47 | self.file_len = 0 48 | self.total_segments = 0 49 | self.data_segments = 0 50 | self.segment_bit_error = 0 51 | self.data_segment_duplicate = 0 52 | self.dup_ACK_sent = 0 53 | 54 | def generate_checksum(self): 55 | checksum = 0 56 | data_len = len(self.recv_data) 57 | data = self.recv_data 58 | if (data_len % 2) == 1: 59 | data_len += 1 60 | data += struct.pack('!B', 0) 61 | 62 | for i in range(0, len(data), 2): 63 | w = (data[i] << 8) + (data[i + 1]) 64 | checksum += w 65 | 66 | checksum = (checksum >> 16) + (checksum & 0xFFFF) 67 | checksum = ~checksum & 0xFFFF 68 | return checksum 69 | 70 | # def verify_checksum(self, checksum): 71 | # data_len = len(self.recv_data) 72 | # data = self.recv_data 73 | # if (data_len % 2) == 1: 74 | # data_len += 1 75 | # data += struct.pack('!B', 0) 76 | # 77 | # for i in range(0, len(data), 2): 78 | # w = (data[i] << 8) + (data[i + 1]) 79 | # checksum += w 80 | # checksum = (checksum >> 16) + (checksum & 0xFFFF) 81 | # if checksum == 0xFFFF: 82 | # return True 83 | # else: 84 | # return False 85 | 86 | def pack_data(self): 87 | """ 88 | Payload is bytes object. 89 | :return: 90 | ---------------------------------------------------------------------------------------------------------------- 91 | Header format: 12 fields. 92 | << (src IP, src Port)|(dest IP, dest Port)| seq num | ack num | SYN | FIN | MWS | MSS | payload | payload len | 93 | checksum| FINACK>> 94 | ---------------------------------------------------------------------------------------------------------------- 95 | """ 96 | src_ip_port = (self.source_IP, self.source_port) 97 | dest_ip_port = (self.destination_IP, self.destination_port) 98 | self.len_send_data = len(self.send_data) 99 | head_info = [src_ip_port, dest_ip_port, self.Sequence_num, self.ACK_num, self.SYN, self.FIN, self.MWS, self.MSS, 100 | self.send_data, self.len_send_data, self.checksum, self.FINACK] 101 | return pickle.dumps(head_info) 102 | 103 | def unpack_data(self): 104 | # need to unpack Acknowledgement segments with no data in it sent by receiver, maybe set self.len_data to 0 105 | self.recv_data = pickle.loads(self.recv_data) 106 | (self.source_IP, self.source_port) = self.recv_data[1] 107 | (self.destination_IP, self.destination_port) = self.recv_data[0] 108 | self.sender_seq, self.recv_ACK, self.SYN, self.FIN, self.MWS, self.MSS = self.recv_data[2:8] 109 | self.checksum = self.recv_data[10] 110 | self.FINACK = self.recv_data[11] 111 | self.len_recv_data, self.recv_data = self.recv_data[9], self.recv_data[8] 112 | 113 | 114 | def receiver_log_file(event, time_T, type_of_pckt, seq_num, num_of_data, ack_num): 115 | with open('Receiver_log.txt', 'a+') as receiver_log: 116 | receiver_log.write(f"{event:<10}{time_T:>30.3f}{type_of_pckt:^10}{seq_num:<20}{num_of_data:<20}{ack_num:<20}\n") 117 | 118 | 119 | def write_statistics(info): 120 | with open('Receiver_log.txt', 'a+') as receiver_log: 121 | receiver_log.write("====================================================\n") 122 | receiver_log.write("Amount of data received (bytes)".ljust(28) + f"{info[0]}\n".rjust(72)) 123 | receiver_log.write("Total Segments Received".ljust(28) + f"{info[1]}\n".rjust(72)) 124 | receiver_log.write("Data Segments Received".ljust(28) + f"{info[2]}\n".rjust(72)) 125 | receiver_log.write("Data Segments with Bit Errors".ljust(28) + f"{info[3]}\n".rjust(72)) 126 | receiver_log.write("Duplicate Data Segments Received".ljust(28) + f"{info[4]}\n".rjust(72)) 127 | receiver_log.write("Duplicate ACKs Sent".ljust(28) + f"{info[5]}\n".rjust(72)) 128 | receiver_log.write("====================================================\n") 129 | 130 | 131 | def generate_PDF_copy(file_name, content): 132 | with open(file_name, 'wb+') as pdf_copy: 133 | pdf_copy.write(content) 134 | 135 | 136 | def three_way_handshaking(receiver_socket, head, dest_ip_port): 137 | head.recv_data = receiver_socket.recvfrom(4096)[0] 138 | recv_S_time = round((time.time() - head.start_time), 3) 139 | head.unpack_data() 140 | if head.SYN == 1: 141 | receiver_log_file('rcv', recv_S_time, 'S', head.sender_seq, head.len_recv_data, head.ACK_num) 142 | head.SYN = 1 143 | head.ACK_num = head.sender_seq + 1 144 | receiver_socket.sendto(head.pack_data(), dest_ip_port) 145 | time_stamp = round((time.time() - head.start_time), 3) 146 | receiver_log_file('snd', time_stamp, 'SA', head.Sequence_num, head.len_send_data, head.ACK_num) 147 | head.recv_data = receiver_socket.recvfrom(4096)[0] 148 | time_stamp = round((time.time() - head.start_time), 3) 149 | head.unpack_data() 150 | if head.ACK_num == head.Sequence_num + 1: 151 | receiver_log_file('rcv', time_stamp, 'A', head.sender_seq, head.len_recv_data, head.ACK_num) 152 | head.Sequence_num = head.ACK_num 153 | head.state = 'connected' 154 | head.total_segments += 2 155 | 156 | 157 | def find_gap(ACK_num, MSS): 158 | gap = 1 159 | while gap in ACK_num: 160 | gap += MSS 161 | return gap 162 | 163 | 164 | def main_function(): 165 | state_header = Header() 166 | state_header.source_IP = get_host_IP() 167 | state_header.source_port = int(sys.argv[1]) 168 | state_header.file_name = sys.argv[2] 169 | recv_socket = socket(AF_INET, SOCK_DGRAM) 170 | state_header.start_time = time.time() 171 | recv_socket.bind(('', state_header.source_port)) 172 | print(f"receiver is running on {state_header.source_IP}, using {state_header.source_port} as its port") 173 | # this just receiver the whole length of the file and the address info about the sender. 174 | file_len, addr = recv_socket.recvfrom(1024) 175 | state_header.file_len = int(file_len.decode()) 176 | state_header.destination_IP, state_header.destination_port = addr[0], addr[1] 177 | # file_dict stores the whole file bytes 178 | file_dict = dict() 179 | ACK_num = [] 180 | 181 | while True: 182 | # handshaking with the sender. 183 | if state_header.state == 'closed': 184 | three_way_handshaking(recv_socket, state_header, addr) 185 | file_len = state_header.file_len // state_header.MSS 186 | file_len += 1 187 | print("Receiving data, please wait patiently...") 188 | # data transmission 189 | if state_header.state == 'connected': 190 | state_header.recv_data, addr = recv_socket.recvfrom(4096) 191 | time_stamp = round(time.time() - state_header.start_time, 3) 192 | state_header.unpack_data() 193 | checksum = state_header.generate_checksum() 194 | if checksum == state_header.checksum: 195 | # print a progress bar to make it easier for us to see the process 196 | progres = len(list(set(ACK_num))) 197 | print('\r[' + '>>' * (int(progres / file_len * 20)) + ' ' * (20 - int(progres / file_len * 20)) * 2 + 198 | '] ' + str(int(progres / file_len * 100)) + '%', end='') 199 | data_num = state_header.sender_seq - 1 200 | # FIN segment received 201 | if state_header.FIN == 1: 202 | state_header.state = 'FINACK' 203 | receiver_log_file('rcv', time_stamp, 'F', state_header.sender_seq, state_header.len_recv_data, 204 | state_header.recv_ACK) 205 | continue 206 | # data segment received 207 | receiver_log_file('rcv', time_stamp, 'D', state_header.sender_seq, state_header.len_recv_data, 208 | state_header.recv_ACK) 209 | # duplicate data segment recieved 210 | if data_num in file_dict: 211 | state_header.data_segment_duplicate += 1 212 | state_header.data_segments += 1 213 | state_header.total_segments += 1 214 | file_dict[state_header.sender_seq - 1] = state_header.recv_data 215 | ACK_num.append(state_header.sender_seq) 216 | gap_point = find_gap(ACK_num, state_header.MSS) 217 | max_ACK = max(ACK_num) 218 | # no gap between received ACK 219 | if gap_point > max_ACK: 220 | data_len = len(file_dict[max_ACK - 1]) 221 | state_header.ACK_num = max_ACK + data_len 222 | # print("ACK_number", state_header.ACK_num) 223 | state_header.checksum = 0 224 | recv_socket.sendto(state_header.pack_data(), addr) 225 | time_stamp = round(time.time() - state_header.start_time, 3) 226 | receiver_log_file('snd', time_stamp, 'A', state_header.Sequence_num, state_header.len_send_data, 227 | state_header.ACK_num) 228 | # there is gap between received ACK 229 | else: 230 | state_header.ACK_num = gap_point 231 | state_header.checksum = 0 232 | recv_socket.sendto(state_header.pack_data(), addr) 233 | time_stamp = round(time.time() - state_header.start_time, 3) 234 | receiver_log_file('snd/DA', time_stamp, 'A', state_header.Sequence_num, state_header.len_send_data, 235 | state_header.ACK_num) 236 | state_header.dup_ACK_sent += 1 237 | # corrupted data segment received 238 | else: 239 | receiver_log_file('rcv/corr', time_stamp, 'D', state_header.sender_seq, state_header.len_recv_data, 240 | state_header.recv_ACK) 241 | state_header.segment_bit_error += 1 242 | state_header.total_segments += 1 243 | 244 | if state_header.state == 'FINACK': 245 | # ACK 246 | state_header.ACK_num = state_header.sender_seq + 1 247 | state_header.FINACK = 1 248 | state_header.checksum = 0 249 | recv_socket.sendto(state_header.pack_data(), addr) 250 | time_stamp = round(time.time() - state_header.start_time, 3) 251 | receiver_log_file('snd', time_stamp, 'A', state_header.Sequence_num, state_header.len_send_data, 252 | state_header.ACK_num) 253 | # FIN 254 | state_header.FIN = 1 255 | state_header.checksum = 0 256 | recv_socket.sendto(state_header.pack_data(), addr) 257 | time_stamp = round(time.time() - state_header.start_time, 3) 258 | receiver_log_file('snd', time_stamp, 'F', state_header.Sequence_num, state_header.len_send_data, 259 | state_header.ACK_num) 260 | state_header.state = 'senderACK' 261 | if state_header.state == 'senderACK': 262 | state_header.recv_data = recv_socket.recvfrom(1024)[0] 263 | time_stamp = round(time.time() - state_header.start_time, 3) 264 | state_header.unpack_data() 265 | receiver_log_file('rcv', time_stamp, 'A', state_header.sender_seq, state_header.len_recv_data, 266 | state_header.recv_ACK) 267 | state_header.state = 'finished' 268 | state_header.total_segments += 2 269 | recv_socket.close() 270 | break 271 | if state_header.state == 'finished': 272 | # assemble the content 273 | content_copy = b'' 274 | for key in sorted(file_dict.keys()): 275 | content_copy += file_dict[key] 276 | generate_PDF_copy(state_header.file_name, content_copy) 277 | # write statistics into receiver log file 278 | print("\nFile received successfully, writing statistics into receiver log file.") 279 | statistics = [state_header.file_len, state_header.total_segments, state_header.data_segments, 280 | state_header.segment_bit_error, state_header.data_segment_duplicate, state_header.dup_ACK_sent] 281 | write_statistics(statistics) 282 | print("All done.") 283 | 284 | 285 | if __name__ == '__main__': 286 | main_function() -------------------------------------------------------------------------------- /assignment1/sender.py: -------------------------------------------------------------------------------- 1 | # comp9331 assignment written by Maowen Zhou 2 | # Using python 3.7 3 | from socket import * 4 | import time 5 | import sys 6 | import random 7 | import pickle 8 | import struct 9 | import threading 10 | 11 | 12 | class Header: 13 | def __init__(self): 14 | self.file_name = sys.argv[3] 15 | self.source_IP, self.source_port = get_host_IP_port() 16 | self.destination_IP = sys.argv[1] 17 | self.destination_port = int(sys.argv[2]) 18 | self.Sequence_num = 0 19 | self.receiver_seq = 0 20 | self.ACK_num = 0 21 | self.SYN = 0 22 | self.FIN = 0 23 | self.FINACK = 0 24 | self.MWS = int(sys.argv[4]) 25 | self.MSS = int(sys.argv[5]) 26 | self.gamma = float(sys.argv[6]) 27 | self.state = 'closed' 28 | self.pDrop = float(sys.argv[7]) 29 | self.pDuplicate = float(sys.argv[8]) 30 | self.pCorrupt = float(sys.argv[9]) 31 | self.pOrder = float(sys.argv[10]) 32 | # maxOrder between [1, 6] 33 | self.maxOrder = int(sys.argv[11]) 34 | self.pDelay = float(sys.argv[12]) 35 | # maxDelay (ms) 36 | self.maxDelay = float(sys.argv[13]) / 1000 37 | self.seed = int(sys.argv[-1]) 38 | # 39 | self.send_data = b'' 40 | self.checksum = 0 41 | self.recv_data = b'' 42 | self.len_send_data = 0 43 | self.len_recv_data = 0 44 | self.send_time = 0 45 | # timeout (s) 46 | self.timeout = (500 + self.gamma * 250) / 1000 47 | # for the log file 48 | self.start_time = 0 49 | self.file_len = 0 50 | 51 | # this part of code is borrowed from the web 52 | def generate_checksum(self): 53 | checksum = 0 54 | data_len = len(self.send_data) 55 | data = self.send_data 56 | if (data_len % 2) == 1: 57 | data_len += 1 58 | data += struct.pack('!B', 0) 59 | 60 | for i in range(0, len(data), 2): 61 | w = (data[i] << 8) + (data[i + 1]) 62 | checksum += w 63 | 64 | checksum = (checksum >> 16) + (checksum & 0xFFFF) 65 | checksum = ~checksum & 0xFFFF 66 | self.checksum = checksum 67 | 68 | def pack_data(self): 69 | """ 70 | Payload is bytes object. 71 | ---------------------------------------------------------------------------------------------------------------- 72 | Header format: 12 fields. 73 | << (src IP, src Port)|(dest IP, dest Port)| seq num | ack num | SYN | FIN | MWS | MSS | payload | payload len | 74 | checksum| FINACK>> 75 | ---------------------------------------------------------------------------------------------------------------- 76 | """ 77 | src_ip_port = (self.source_IP, self.source_port) 78 | dest_ip_port = (self.destination_IP, self.destination_port) 79 | self.len_send_data = len(self.send_data) 80 | head_info = [src_ip_port, dest_ip_port, self.Sequence_num, self.ACK_num, self.SYN, self.FIN, self.MWS, self.MSS, 81 | self.send_data, self.len_send_data, self.checksum, self.FINACK] 82 | return pickle.dumps(head_info) 83 | 84 | def unpack_data(self): 85 | # need to unpack Acknowledgement segments with no data in it sent by receiver, maybe set self.len_data to 0 86 | self.recv_data = pickle.loads(self.recv_data) 87 | (self.source_IP, self.source_port) = self.recv_data[1] 88 | (self.destination_IP, self.destination_port) = self.recv_data[0] 89 | self.receiver_seq, self.ACK_num, self.SYN, self.FIN, self.MWS, self.MSS = self.recv_data[2:8] 90 | self.checksum = self.recv_data[10] 91 | self.FINACK = self.recv_data[11] 92 | self.len_recv_data, self.recv_data = self.recv_data[9], self.recv_data[8] 93 | 94 | 95 | class Timer: 96 | def __init__(self, duration): 97 | self.start_time = 0 98 | self.started = False 99 | self.tracking = 1 100 | self.duration = duration 101 | 102 | def stop(self): 103 | self.started = False 104 | self.start_time = 0 105 | 106 | def start(self): 107 | assert(not self.started) 108 | self.start_time = time.time() 109 | self.started = True 110 | 111 | def restart(self): 112 | if self.started: 113 | self.stop() 114 | self.start() 115 | 116 | def time(self): 117 | if self.started: 118 | return time.time() - self.start_time 119 | 120 | def timeout(self): 121 | if self.started: 122 | if time.time() - self.start_time >= self.duration: 123 | return True 124 | return False 125 | 126 | 127 | def get_host_IP_port(): 128 | """ 129 | Elegantly get the IP address and port number of the sender machine 130 | """ 131 | s = socket(AF_INET, SOCK_DGRAM) 132 | try: 133 | s.connect(('8.8.8.8', 80)) 134 | host_ip, host_port = s.getsockname() 135 | finally: 136 | s.close() 137 | return host_ip, host_port 138 | 139 | 140 | def read_PDF_file(file_name): 141 | """ 142 | Get the content of the PDF file in byte form 143 | """ 144 | try: 145 | with open(file_name, 'rb') as file_to_send: 146 | content_of_file = file_to_send.read() 147 | return content_of_file 148 | except FileNotFoundError: 149 | print(f"No file called {file_name}in the current directory.") 150 | 151 | 152 | def chop_up_file(file_content, MSS): 153 | """ 154 | Generate a dictionary, where key is like sequence number, value is byte_data with the size of MSS 155 | """ 156 | content_len = len(file_content) 157 | content_list = [file_content[i:i + MSS] for i in range(0, content_len, MSS)] 158 | content_list_len = len(content_list) 159 | content_dict = dict() 160 | for i in range(content_list_len): 161 | content_dict[i * MSS] = content_list[i] 162 | return content_dict 163 | 164 | 165 | def valid_IP_Port(ip_address, port): 166 | """ 167 | Make sure the IP address is in the correct format 168 | """ 169 | num_list = ip_address.split('.') 170 | if len(num_list) != 4: 171 | return False 172 | for i in num_list: 173 | if int(i) > 255: 174 | return False 175 | if not (10000 <= port <= 65536): 176 | return False 177 | return True 178 | 179 | 180 | def sender_log_file(event, time_T, type_of_pckt, seq_num, num_of_data, ack_num): 181 | with open("Sender_log.txt", 'a+') as sender_log: 182 | sender_log.write(f"{event:<10}{time_T:<30.3f}{type_of_pckt:^10}{seq_num:<20}" 183 | f"{num_of_data:<20}{ack_num:<20}\n") 184 | 185 | 186 | def write_statistics(statistics): 187 | """ 188 | Write the statistics of the send process at the end of the file 189 | """ 190 | with open('Sender_log.txt', 'a+') as sender_log: 191 | sender_log.write("====================================================\n") 192 | sender_log.write("Size of the file (in Bytes)".ljust(28) + f"{statistics[0]}\n".rjust(72)) 193 | sender_log.write("Segments transmitted (including drop & RXT)".ljust(28) + f"{statistics[1]}\n".rjust(39)) 194 | sender_log.write("Number of Segments handled by PLD".ljust(28) + f"{statistics[2]}\n".rjust(51)) 195 | sender_log.write("Number of Segments dropped".ljust(28) + f"{statistics[3]}\n".rjust(60)) 196 | sender_log.write("Number of Segments Corrupted".ljust(28) + f"{statistics[4]}\n".rjust(60)) 197 | sender_log.write("Number of Segments Re-ordered".ljust(28) + f"{statistics[5]}\n".rjust(60)) 198 | sender_log.write("Number of Segments Duplicated".ljust(28) + f"{statistics[6]}\n".rjust(60)) 199 | sender_log.write("Number of Segments Delayed".ljust(28) + f"{statistics[7]}\n".rjust(60)) 200 | sender_log.write("Number of Retransmissions due to TIMEOUT".ljust(28) + f"{statistics[8]}\n".rjust(41)) 201 | sender_log.write("Number of FAST RETRANSMISSION".ljust(28) + f"{statistics[9]}\n".rjust(60)) 202 | sender_log.write("Number of DUP ACKs received".ljust(28) + f"{statistics[10]}\n".rjust(60)) 203 | sender_log.write("====================================================\n") 204 | 205 | 206 | def three_way_handshaking(sender_socket, head, dest_ip_port): 207 | """ 208 | """ 209 | global segment_sent 210 | # Initiate the connection 211 | head.SYN = 1 212 | head.FIN = 0 213 | head.generate_checksum() 214 | sender_socket.sendto(head.pack_data(), dest_ip_port) 215 | start_connection_time = round(time.time() - head.start_time, 3) 216 | sender_log_file('snd', start_connection_time, 'S', head.Sequence_num, head.len_send_data, head.ACK_num) 217 | head.recv_data, addr = sender_socket.recvfrom(1024) 218 | recv_SA_time = round(time.time() - head.start_time, 3) 219 | head.unpack_data() 220 | # when receiving segments source_ip_port and dest_ip_port are swapped. 221 | sender_log_file('rcv', recv_SA_time, 'SA', head.receiver_seq, head.len_recv_data, head.ACK_num) 222 | if head.ACK_num == head.Sequence_num + 1: 223 | print("Connection is established, ready to transmit data.") 224 | head.SYN = 0 225 | head.Sequence_num = head.ACK_num 226 | head.ACK_num = head.receiver_seq + 1 227 | head.receiver_seq += 1 228 | head.pack_data() 229 | head.generate_checksum() 230 | sender_socket.sendto(head.pack_data(), addr) 231 | time_stamp = round(time.time() - head.start_time, 3) 232 | sender_log_file('snd', time_stamp, 'A', head.Sequence_num, head.len_send_data, head.ACK_num) 233 | head.state = 'connected' 234 | segment_sent += 2 235 | 236 | 237 | def PLD_module(state_head, sender_socket, dest_ip_port): 238 | odds = random.random() 239 | reorder_holder = set() 240 | reorder_counter = 0 241 | reorder_head = Header() 242 | reorder_head.start_time = state_head.start_time 243 | global segment_dropped, segment_corrupted, sent_Seq 244 | global segment_reorderd, segment_duplicated, segment_delayed 245 | 246 | if odds < state_head.pDrop: 247 | time_stamp = round(time.time() - state_head.start_time, 3) 248 | sender_log_file('drop', time_stamp, 'D', state_head.Sequence_num, state_head.len_send_data, 249 | state_head.ACK_num) 250 | segment_dropped += 1 251 | reorder_counter += 1 252 | elif odds < state_head.pDuplicate: 253 | time_stamp = round(time.time() - state_head.start_time, 3) 254 | sender_socket.sendto(state_head.pack_data(), dest_ip_port) 255 | sender_log_file('snd', time_stamp, 'D', state_head.Sequence_num, state_head.len_send_data, 256 | state_head.ACK_num) 257 | time_stamp = round(time.time() - state_head.start_time, 3) 258 | sender_socket.sendto(state_head.pack_data(), dest_ip_port) 259 | sender_log_file('snd/dup', time_stamp, 'D', state_head.Sequence_num, state_head.len_send_data, 260 | state_head.ACK_num) 261 | reorder_counter += 2 262 | segment_duplicated += 1 263 | elif odds < state_head.pCorrupt: 264 | time_stamp = round(time.time() - state_head.start_time, 3) 265 | # introduce 1 bit error 266 | state_head.send_data = state_head.send_data[:-1] + b'*' 267 | sender_socket.sendto(state_head.pack_data(), dest_ip_port) 268 | sender_log_file('snd/corr', time_stamp, 'D', state_head.Sequence_num, state_head.len_send_data, 269 | state_head.ACK_num) 270 | reorder_counter += 1 271 | segment_corrupted += 1 272 | elif odds < state_head.pOrder: 273 | if not reorder_holder: 274 | reorder_holder.add(state_head.Sequence_num) 275 | segment_reorderd += 1 276 | reorder_head.Sequence_num = state_head.Sequence_num 277 | reorder_head.send_data = state_head.send_data 278 | reorder_head.len_send_data = len(reorder_head.send_data) 279 | reorder_head.receiver_seq = state_head.receiver_seq 280 | reorder_head.ACK_num = state_head.receiver_seq + reorder_head.len_recv_data 281 | elif reorder_holder: 282 | sender_socket.sendto(state_head.pack_data(), dest_ip_port) 283 | time_stamp = round(time.time() - state_head.start_time, 3) 284 | sender_log_file('snd', time_stamp, 'D', state_head.Sequence_num, 285 | state_head.len_send_data, state_head.ACK_num) 286 | reorder_counter += 1 287 | if reorder_counter > state_head.maxOrder: 288 | reorder_counter = 0 289 | reorder_holder.pop() 290 | sender_socket.sendto(reorder_head.pack_data(), dest_ip_port) 291 | time_stamp = round(time.time() - reorder_head.start_time, 3) 292 | sender_log_file('snd/rord', time_stamp, 'D', reorder_head.Sequence_num, 293 | reorder_head.len_send_data, reorder_head.ACK_num) 294 | elif odds < state_head.pDelay: 295 | time.sleep(state_head.maxDelay) 296 | time_stamp = round(time.time() - state_head.start_time, 3) 297 | sender_socket.sendto(state_head.pack_data(), dest_ip_port) 298 | sender_log_file('snd/dely', time_stamp, 'D', state_head.Sequence_num, state_head.len_send_data, 299 | state_head.ACK_num) 300 | reorder_counter += 1 301 | segment_delayed += 1 302 | else: 303 | time_stamp = round(time.time() - state_head.start_time, 3) 304 | sender_socket.sendto(state_head.pack_data(), dest_ip_port) 305 | if state_head.Sequence_num in sent_Seq: 306 | sender_log_file('snd/RXT', time_stamp, 'D', state_head.Sequence_num, 307 | state_head.len_send_data, state_head.ACK_num) 308 | else: 309 | sender_log_file('snd', time_stamp, 'D', state_head.Sequence_num, 310 | state_head.len_send_data, state_head.ACK_num) 311 | reorder_counter += 1 312 | sent_Seq.add(state_head.Sequence_num) 313 | 314 | 315 | def Sending(sender_socket, file_dict, dest_ip_port): 316 | # data transmitting 317 | global SendBase, NextSeqNum, timer, state_head 318 | global received_ACK, sent_Seq 319 | global timer, segment_PLD 320 | global segment_sent, recv_flag 321 | global RXT_timeout, sampleRTT 322 | 323 | sent_Seq = set() 324 | received_ACK = 0 325 | timer = Timer(state_head.timeout) 326 | sampleRTT = Timer(0) 327 | RXT_head = Header() 328 | RXT_head.receiver_seq = 1 329 | RXT_head.start_time = state_head.start_time 330 | while True: 331 | if received_ACK - 1 == state_head.file_len: 332 | break 333 | while NextSeqNum < SendBase + state_head.MWS and NextSeqNum < state_head.file_len: 334 | state_head.Sequence_num = NextSeqNum 335 | state_head.send_data = file_dict[NextSeqNum - 1] 336 | # generate checksum just for the data 337 | state_head.generate_checksum() 338 | state_head.len_send_data = len(state_head.send_data) 339 | state_head.ACK_num = state_head.receiver_seq + state_head.len_recv_data 340 | PLD_module(state_head, sender_socket, dest_ip_port) 341 | if not timer.started: 342 | timer.start() 343 | timer.tracking = SendBase 344 | # keep track of Seq handled by PLD 345 | NextSeqNum += state_head.len_send_data 346 | # if (not sampleRTT.started) and (NextSeqNum not in sent_Seq): 347 | # sampleRTT.start() 348 | # sampleRTT.tracking = NextSeqNum 349 | segment_sent += 1 350 | segment_PLD += 1 351 | # timeout and retrasmit 352 | if timer.timeout() and recv_flag == 0: 353 | RXT_head.Sequence_num = timer.tracking 354 | RXT_head.send_data = file_dict[timer.tracking - 1] 355 | # generate checksum just for the data 356 | RXT_head.generate_checksum() 357 | RXT_head.len_send_data = len(RXT_head.send_data) 358 | RXT_head.ACK_num = RXT_head.receiver_seq + RXT_head.len_recv_data 359 | PLD_module(RXT_head, sender_socket, dest_ip_port) 360 | timer.restart() 361 | RXT_timeout += 1 362 | segment_sent += 1 363 | segment_PLD += 1 364 | 365 | 366 | def Receiving(recv_head, sender_socket, file_dict, dest_ip_port): 367 | global EstimatedRTT, DevRTT, SendBase, NextSeqNum 368 | global timer, state_head, ACK_count 369 | global received_ACK, recv_flag 370 | global segment_sent, segment_PLD 371 | global fast_RXT, dup_ACK, sampleRTT 372 | 373 | ACK_count = 0 374 | while True: 375 | recv_flag = 0 376 | recv_head.recv_data = sender_socket.recvfrom(1024)[0] 377 | time_stamp = round(time.time() - state_head.start_time, 3) 378 | recv_head.unpack_data() 379 | received_ACK = recv_head.ACK_num 380 | # file transmission complete 381 | if received_ACK - 1 == state_head.file_len: 382 | sender_log_file('rcv', time_stamp, 'A', recv_head.receiver_seq, 383 | recv_head.len_recv_data, recv_head.ACK_num) 384 | print("PDF file has been successfully transmitted, about to terminate the connection.") 385 | state_head.state = 'FIN' 386 | if state_head.state == 'FIN': 387 | # send FIN 388 | state_head.FIN = 1 389 | state_head.Sequence_num = recv_head.ACK_num 390 | state_head.send_data = b'' 391 | state_head.len_send_data = len(state_head.send_data) 392 | state_head.ACK_num = state_head.receiver_seq + state_head.len_send_data 393 | time_stamp = round(time.time() - state_head.start_time, 3) 394 | state_head.generate_checksum() 395 | sender_socket.sendto(state_head.pack_data(), dest_ip_port) 396 | sender_log_file('snd', time_stamp, 'F', state_head.Sequence_num, state_head.len_send_data, 397 | state_head.ACK_num) 398 | state_head.state = 'FINACK' 399 | if state_head.state == 'FINACK': 400 | recv_head.recv_data = sender_socket.recvfrom(1024)[0] 401 | time_stamp = round(time.time() - state_head.start_time, 3) 402 | recv_head.unpack_data() 403 | if recv_head.FINACK == 1: 404 | sender_log_file('rcv', time_stamp, 'A', recv_head.receiver_seq, recv_head.len_recv_data, 405 | recv_head.ACK_num) 406 | state_head.state = 'receiverFIN' 407 | if state_head.state == 'receiverFIN': 408 | recv_head.recv_data = sender_socket.recvfrom(1024)[0] 409 | time_stamp = round(time.time() - state_head.start_time, 3) 410 | recv_head.unpack_data() 411 | if recv_head.FIN == 1: 412 | sender_log_file('rcv', time_stamp, 'F', recv_head.receiver_seq, recv_head.len_recv_data, 413 | recv_head.ACK_num) 414 | state_head.state = 'senderACK' 415 | if state_head.state == 'senderACK': 416 | state_head.FIN = 0 417 | state_head.Sequence_num = recv_head.ACK_num 418 | state_head.ACK_num = state_head.receiver_seq + 1 419 | state_head.generate_checksum() 420 | sender_socket.sendto(state_head.pack_data(), dest_ip_port) 421 | time_stamp = round(time.time() - state_head.start_time, 3) 422 | sender_log_file('snd', time_stamp, 'A', state_head.Sequence_num, state_head.len_send_data, 423 | state_head.ACK_num) 424 | segment_sent += 2 425 | statistics = [state_head.file_len, segment_sent, segment_PLD, segment_dropped, segment_corrupted, 426 | segment_reorderd, segment_duplicated, segment_delayed, RXT_timeout, fast_RXT, dup_ACK] 427 | print("Writing statistics into Sender log file....") 428 | write_statistics(statistics) 429 | print("All done.") 430 | if timer.started: 431 | timer.stop() 432 | sender_socket.close() 433 | break 434 | else: 435 | # receive ACKs 436 | if received_ACK > SendBase: 437 | # refresh the timeout value 438 | # len_tracking_data = len(file_dict[sampleRTT.tracking - 1]) 439 | # # print("sampleRTT.tracking->", sampleRTT.tracking) 440 | # print("timer.duration->", timer.duration) 441 | # if sampleRTT.started and sampleRTT.tracking + len_tracking_data == received_ACK: 442 | # # print("received_ACK->", received_ACK) 443 | # time_value = sampleRTT.time() * 1000 444 | # # print(time_value/1000) 445 | # EstimatedRTT = 0.875 * EstimatedRTT + 0.125 * time_value 446 | # DevRTT = 0.75 * DevRTT + 0.25 * abs(time_value - EstimatedRTT) 447 | # calculated_interval = (EstimatedRTT + state_head.gamma * DevRTT) / 1000 448 | # if 0 < calculated_interval <= 60: 449 | # timer.duration = calculated_interval 450 | # sampleRTT.stop() 451 | sender_log_file('rcv', time_stamp, 'A', recv_head.receiver_seq, 452 | recv_head.len_recv_data, recv_head.ACK_num) 453 | SendBase = received_ACK 454 | if timer.started: 455 | timer.tracking = SendBase 456 | timer.restart() 457 | ACK_count = 0 458 | recv_flag = 1 459 | # receive duplicate ACK 460 | else: 461 | ACK_count += 1 462 | sender_log_file('rcv/DA', time_stamp, 'A', recv_head.receiver_seq, 463 | recv_head.len_recv_data, recv_head.ACK_num) 464 | dup_ACK += 1 465 | # fast retransmit 466 | if ACK_count == 3: 467 | RXT_head = Header() 468 | RXT_head.receiver_seq = 1 469 | RXT_head.start_time = state_head.start_time 470 | RXT_head.Sequence_num = timer.tracking 471 | RXT_head.send_data = file_dict[timer.tracking - 1] 472 | # generate checksum just for the data 473 | RXT_head.generate_checksum() 474 | RXT_head.len_send_data = len(RXT_head.send_data) 475 | RXT_head.ACK_num = RXT_head.receiver_seq + RXT_head.len_recv_data 476 | PLD_module(RXT_head, sender_socket, dest_ip_port) 477 | timer.restart() 478 | fast_RXT += 1 479 | segment_sent += 1 480 | segment_PLD += 1 481 | 482 | 483 | def main_function(): 484 | global state_head 485 | global segment_sent, segment_PLD, segment_dropped, segment_corrupted 486 | global segment_reorderd, segment_duplicated, segment_delayed 487 | global RXT_timeout, fast_RXT, dup_ACK 488 | segment_sent = 0 489 | segment_PLD = 0 490 | segment_dropped = 0 491 | segment_corrupted = 0 492 | segment_reorderd = 0 493 | segment_duplicated = 0 494 | segment_delayed = 0 495 | RXT_timeout = 0 496 | fast_RXT = 0 497 | dup_ACK = 0 498 | state_head = Header() 499 | try: 500 | if not valid_IP_Port(state_head.destination_IP, state_head.destination_port): 501 | raise ValueError 502 | except ValueError: 503 | print("Sorry, incorrect IP address or Port number, safe Port number [10000 <-> 65536].") 504 | sys.exit() 505 | # read PDF file 506 | file_content = read_PDF_file(state_head.file_name) 507 | state_head.file_len = len(file_content) 508 | file_dict = chop_up_file(file_content, state_head.MSS) 509 | sender_socket = socket(AF_INET, SOCK_DGRAM) 510 | random.seed(state_head.seed) 511 | state_head.start_time = time.time() 512 | dest_ip_port = (state_head.destination_IP, state_head.destination_port) 513 | # send the len of the file to the receiver 514 | sender_socket.sendto(str(state_head.file_len).encode(), dest_ip_port) 515 | recv_head = Header() 516 | recv_head.start_time = state_head.start_time 517 | global EstimatedRTT, DevRTT, SendBase, NextSeqNum 518 | SendBase = 1 519 | NextSeqNum = 1 520 | EstimatedRTT = 500 521 | DevRTT = 250 522 | # three way handshake 523 | if state_head.state == 'closed': 524 | three_way_handshaking(sender_socket, state_head, dest_ip_port) 525 | print("Transmitting the file, please wait patiently.") 526 | 527 | sending = threading.Thread(target=Sending, args=(sender_socket, file_dict, dest_ip_port)) 528 | receiving = threading.Thread(target=Receiving, args=(recv_head, sender_socket, file_dict, dest_ip_port)) 529 | for proc in (sending, receiving): 530 | proc.start() 531 | for proc in (sending, receiving): 532 | proc.join() 533 | 534 | 535 | if __name__ == '__main__': 536 | main_function() --------------------------------------------------------------------------------