class BTCPServerSocket(BTCPSocket): def __init__(self, window, timeout): super().__init__(window, timeout) self._lossy_layer = LossyLayer(self, SERVER_IP, SERVER_PORT, CLIENT_IP, CLIENT_PORT) self._active = True # Called by the lossy layer from another thread whenever a segment arrives def lossy_layer_input(self, segment): rsegment = segment[0] s = bTCPSegment() s.decode(segment[0]) if self.status < 3: self.accept(s) else: if SegmentType.FIN in s.flags: # Disconnected self._active = False self.disconnect() else: self.process_message(s, rsegment) # Wait for the client to initiate a three-way handshake def accept(self, client_segment): if self.status == 3: raise AttributeError( "Attempt to establish connection when connection is already (being) established." ) if SegmentType.ACK in client_segment.flags: self.status = 3 else: self._seqnum = int.from_bytes(urandom(2), byteorder) self._acknum = client_segment.seqnumber + 1 newsegment = bTCPSegment().Factory() newsegment.setFlag(SegmentType.SYN) newsegment.setFlag(SegmentType.ACK) newsegment.setAcknowledgementNumber(self._acknum) newsegment.setSequenceNumber(self._seqnum) newsegment.setWindow(self._window) newsegment = newsegment.make() self._lossy_layer.send_segment(newsegment) self.status = 2 self._rwindow = client_segment.window # Reply with FINACK and close connection def disconnect(self): s = bTCPSegment() s = s.Factory() \ .setFlag(SegmentType.ACK) \ .setFlag(SegmentType.FIN) \ .make() self._lossy_layer.send_segment(s) def isActive(self): return self._active
class BTCPClientSocket(BTCPSocket): def __init__(self, window, timeout): super().__init__(window, timeout) self._lossy_layer = LossyLayer(self, CLIENT_IP, CLIENT_PORT, SERVER_IP, SERVER_PORT) self.connloop = asyncio.get_event_loop() self.tries = 1 # Called by the lossy layer from another thread whenever a segment arrives. def lossy_layer_input(self, segment): rsegment = segment[0] s = bTCPSegment() s.decode(segment[0]) if self.status == 1: # Step 2 of handshake self.finish_connect(s) self._rwindow = s.window return if SegmentType.ACK in s.flags and SegmentType.FIN in s.flags: self.status = 0 try: self._lossy_layer.destroy() except RuntimeError: pass else: self.process_message(s, rsegment) # Perform a three-way handshake to establish a connection def connect(self, first=True): if self.status > 1 and first: raise AttributeError( "Attempt to establish connection when connection is already (being) established." ) self._seqnum = int.from_bytes(urandom(2), byteorder) s = bTCPSegment() s = s.Factory() \ .setFlag(SegmentType.SYN) \ .setWindow(self._window) \ .setSequenceNumber(self._seqnum) s = s.make() self._lossy_layer.send_segment(s) self.status = 1 t = Timer(self._timeout, self.timeout, [self.connect]) t.start() def timeout(self, callback): if self.status == 1 or self.status == 4: if self.tries < MAX_TRIES: self.tries += 1 callback(False) else: self.close() raise TimeoutError("Connection to server timed out after " + str(self.tries) + " tries") def finish_connect(self, server_segment): if self.status > 1: raise AttributeError( "Attempt to establish connection when connection is already (being) established." ) self._seqnum += 1 self._acknum = server_segment.seqnumber + 1 s = bTCPSegment() s = s.Factory() \ .setFlag(SegmentType.ACK) \ .setSequenceNumber(self._seqnum) \ .setAcknowledgementNumber(self._acknum) \ .setWindow(self._window) self._lossy_layer.send_segment(s.make()) self.status = 3 # Perform a handshake to terminate a connection def disconnect(self): self.sendFin() self.tries = 1 self.status = 4 t = Timer(self._timeout, self.timeout, [self.disconnect]) t.start() # Sends a FIN package def sendFin(self): segment = bTCPSegment() segment = segment.Factory() \ .setFlag(SegmentType.FIN) \ .make() self._lossy_layer.send_segment(segment)
class BTCPClientSocket(BTCPSocket): def __init__(self, window, timeout): super().__init__(window, timeout) self.window = window self.timeout = timeout self.termination_count = 5 self.thread_executor = ThreadPoolExecutor(max_workers=window) self._lossy_layer = LossyLayer(self, CLIENT_IP, CLIENT_PORT, SERVER_IP, SERVER_PORT) self.state = State.CLOSED # Called by the lossy layer from another thread whenever a segment arrives. def lossy_layer_input(self, segment): segment = btcp.packet.unpack_from_socket(segment) segment_type = segment.packet_type() if (segment_type == "SYN-ACK"): self.state = State.SYN_ACK_RECVD self.thread_executor.submit(self.handshake_ack_thread, segment) elif segment_type == "FIN_ACK": self.state = State.FIN_ACK_RECVD else: pass # Initiate a three-way handshake with the server. def connect(self): self.thread_executor.submit(self.con_establish_thread) self.state = State.SYN_SEND # Send data originating from the application in a reliable way to the server def send(self, data): pass # Perform a handshake to terminate a connection def disconnect(self): pass # Clean up any state def close(self): self.thread_executor.shutdown(wait=True) self._lossy_layer.destroy() print("CLIENT CLOSED") # Initiate termination of connection with server def close_client(self): self.thread_executor.submit(self.con_close_thread) self.close() # Send the response to the server's ACK of the handshake. def handshake_ack_thread(self, segment): seq_nr = segment.get_ack_nr() + 1 ack_nr = segment.get_seq_nr() + 1 segment.up_seq_nr( seq_nr - (ack_nr - 1) ) # remove old seq_nr (which is now ack_nr) and replace by new seq_nr segment.up_ack_nr( ack_nr - seq_nr ) # remove als ack_nr (which is now seq_nr) and replace by new ack_nr segment.set_flags(True, False, False) # set ACK flag self._lossy_layer.send_segment(segment.pack()) # Runnable function to establish a connection with the server def con_establish_thread(self): syn_nr = randint(0, 65535) # random 16-bit integer segment = TCPpacket(syn_nr) segment.set_flags(False, True, False) # set SYN flag send_segment = segment.pack() self._lossy_layer.send_segment(send_segment) while True: time.sleep(self.timeout / 1000) if self.state != State.SYN_ACK_RECVD: self._lossy_layer.send_segment(send_segment) else: self.state = State.HNDSH_COMP break # Runnable function to close the connection with the server def con_close_thread(self): segment = TCPpacket() segment.set_flags(False, False, True) send_segment = segment.pack() self._lossy_layer.send_segment(send_segment) while True: time.sleep(self.timeout / 1000) if self.state != State.FIN_ACK_RECVD and self.termination_count > 0: self._lossy_layer.send_segment(send_segment) self.termination_count -= 1 else: self.state = State.CLOSED break
class BTCPClientSocket(BTCPSocket): # window_size is the size of receive_window of the server, timeout is the timeout time, receive_window contains # packets for the client. window_size = 0 timeout = 0 receive_window = [] def __init__(self, window, timeout): super().__init__(window, timeout) self.timeout = TIMEOUT_TIME self._lossy_layer = LossyLayer(self, CLIENT_IP, CLIENT_PORT, SERVER_IP, SERVER_PORT) # Called by the lossy layer from another thread whenever a segment arrives. def lossy_layer_input(self, segment): packet = segment[0] self.receive_window.append(packet) # Perform a three-way handshake to establish a connection with the server # Returns if the establishment was successful def connect(self): handshake_complete = False current_tries = 0 # Try to perform handshake within number of tries while not handshake_complete and current_tries < TRIES: # Step 1 of three-way handshake # Send packet with x and SYN flag x = random.randint(0, 65535) packet = struct.pack('!H2xB1013x', x, SYN) self._lossy_layer.send_segment(packet) current_tries += 1 # Step 2 of three-way handshake start_time = time.time() while time.time( ) - start_time < self.timeout and not handshake_complete: if self.receive_window: # Receive packet with y, x+1 and SYN+ACK flags cur_segment = self.next_packet() y, x_inc, flags, self.window_size = struct.unpack( '!HHBB', cur_segment[:6]) # Step 3 of the three-way handshake if (flags == SYN + ACK) and (x_inc == x + 1): # Send packet with x+1, y+1 and ACK flag, complete handshake packet = struct.pack('!HHB1013x', x_inc, y + 1, ACK) self._lossy_layer.send_segment(packet) handshake_complete = True return handshake_complete # Send data originating from the application in a reliable way to the server using Go-Back-N def send(self, input_data): # remaining_ack is the number of packets that still need to be acknowledged remaining_acks = self.window_size finished = False # Convert input_data to string, then fill packets with packets meant for the server. remaining_data contains # the data not in packets, seq_num the sequence number of the next to be send packet data = bytes(input_data, 'utf-8') packets, remaining_data, seq_num = self.fill_window(data) # While there is still data to be send, and packets to be acknowledged, execute while not finished: # Send packets that are currently in packets for i in range(len(packets)): self._lossy_layer.send_segment(packets[i]) # Start timer and check if packets arrive current_time = time.time() while time.time() - current_time < self.timeout and not finished: # If the client received a packet if self.receive_window: # Assign head of receive_window to cur_segment, unpack it cur_segment = self.next_packet() ack_num, flags = struct.unpack('!2xHB', cur_segment[:5]) packet_num = self._get_seqnum(packets[0]) # If packet contained an ACK and the corresponding acknowledgement number is larger or equal # to the acknowledgement number that is to be acknowledged, proceed if flags == ACK: if ack_num >= packet_num: # Remove acknowledged packet packets.pop(0) # Timer reset current_time = time.time() # Decrease the number of remaining packets to be acknowledged remaining_acks -= 1 # Check if there is any data left to send if remaining_data: # Construct a new packet with sequence number of the last acknowledged packet plus # the receive window size, append it to packets, then send that new packet seq_num = packet_num + self.window_size new_packet = self._construct_packet( seq_num, remaining_data[:PAYLOAD_SIZE]) packets.append(new_packet) self._lossy_layer.send_segment(new_packet) # Decrease remaining data with the size of a data segment, increment the packets that # need to be acknowledged remaining_data = remaining_data[PAYLOAD_SIZE:] remaining_acks += 1 # If there is no data left to be send, and all sent packets are acknowledged then # the client is finished with sending elif not remaining_data and not remaining_acks: finished = True pass # Construct an array of packets with the window size of the server # Returns a list of size window_size containing packets, data has not been put in packets yet # and the next sequence number def fill_window(self, data): packets = [] remaining = data seq_num = 0 # Fill packets with packets with the corresponding window size for i in range(self.window_size): seq_num = i data_segment = remaining[:PAYLOAD_SIZE] remaining = remaining[PAYLOAD_SIZE:] # If data_segment is not empty, append a new packet with sequence number seq_num # and data_segment to packets if data_segment: packets.append(self._construct_packet(seq_num, data_segment)) return packets, remaining, seq_num + 1 # Construct a single packet with sequence number seq_num containing data # Returns a single packet with sequence number seq_num containing data def _construct_packet(self, seq_num, data): data_length = len(data) # Add padding to data if necessary for i in range(PAYLOAD_SIZE - data_length): data += PADDING # Construct packet with sequence number seq_num, data and checksum 0, then compute the # checksum over that packet pckt_no_chksum = struct.pack('!H4xHH1008s', seq_num, data_length, 0, data) packet = struct.pack('!H4xHH1008s', seq_num, data_length, self.in_cksum(pckt_no_chksum), data) return packet # Return the sequence number of a packet def _get_seqnum(self, packet): return int.from_bytes(packet[:2], "big") # Perform a handshake to terminate a connection def disconnect(self): disconnected = False current_tries = 0 # While the disconnection is not completed and not exceeded the maximum amount of tries while not disconnected and current_tries < TRIES: # Step 1 of handshake, send FIN packet flags = FIN packet = struct.pack('!4xB1013x', flags) self._lossy_layer.send_segment(packet) # Step 2/3 of handshake, receive ACK+FIN then close connection start_time = time.time() while time.time() - start_time < TIMEOUT_TIME and not disconnected: # If there is a packet in the receive window, unpack and check for ACK+FIN flags, if there are # close the connection if self.receive_window: cur_segment = self.next_packet() flags = struct.unpack('!B', cur_segment[4:5])[0] if flags == ACK + FIN: disconnected = True self.close() pass # Return the head packet in the receive window def next_packet(self): return self.receive_window.pop(0) # Clean up any state def close(self): self._lossy_layer.destroy()
class BTCPServerSocket(BTCPSocket): data_buffer = b'' receive_window = [] seqnum_server_packet = 0 seqnum_wanted = 0 last_ack = 0 timeout = 0 window = 0 def __init__(self, window, timeout): super().__init__(window, timeout) self.window = window self.timeout = timeout self._lossy_layer = LossyLayer(self, SERVER_IP, SERVER_PORT, CLIENT_IP, CLIENT_PORT) # Called by the lossy layer from another thread whenever a segment arrives def lossy_layer_input(self, segment): packet = segment[0] # Add the incoming packet to the back of the queue if it is approved # using the checksum if self._verify_cksum(packet): self.receive_window.append(packet) else: print("Checksum failed!") pass # Verify the checksum of packet def _verify_cksum(self, packet): received_chksum = struct.unpack('!H', packet[8:10])[0] if received_chksum == 0: return True buffer = array.array('B', packet) struct.pack_into('!H', buffer, 8, 0) computed_chksum = self.in_cksum(bytes(buffer)) return received_chksum == computed_chksum # Wait for the client to initiate a three-way handshake and perform it def accept(self, cur_segment): handshake_complete = False x, flags = struct.unpack('!H2xB', cur_segment[0:5]) # Create and send SYN + ACK y = random.randint(0, 65535) packet = struct.pack('!HH2B1012x', y, x + 1, SYN + ACK, self.window) self._lossy_layer.send_segment(packet) # Wait for ACK start_time = time.time() while time.time( ) - start_time < self.timeout and not handshake_complete: if self.receive_window: cur_segment = self._next_packet() inc_x, inc_y, flags = struct.unpack('!HHB', cur_segment[:5]) # Complete three-way handshake if x + 1 == inc_x and y + 1 == inc_y and flags == ACK: handshake_complete = True pass # Parse the first in line packet and handle accordingly, either: # Respond to three-way handshake initiation of client # Respond to close initiation of client # send data in payload to the application layer def recv(self): payload = b'' if self.receive_window: cur_segment = self._next_packet() flags = struct.unpack('!B', cur_segment[4:5])[0] # Respond to three-way handshake initiation of client if flags == SYN: self.accept(cur_segment) # Respond to close initiation of client elif flags == FIN: segment = struct.pack('!4xB1013x', ACK + FIN) self._lossy_layer.send_segment(segment) self.close() return False # Send data in payload to the application layer elif flags == NO_FLAGS: # Unpack received package seq_num, data_length, data = struct.unpack( '!H4xH2x1008s', cur_segment) # Check ACK number and store data # Respond with the correct Acknowledgement number, # following the 'Go-Back-N' protocol if self.seqnum_wanted == 0: if seq_num == 0: packet = struct.pack('!HHB1013x', self.seqnum_server_packet, seq_num, ACK) self._lossy_layer.send_segment(packet) self.seqnum_server_packet += 1 self.seqnum_wanted += 1 payload = data[:data_length] else: if seq_num == self.seqnum_wanted: ack_num = seq_num self.last_ack = seq_num self.seqnum_wanted += 1 payload = data[:data_length] else: ack_num = self.last_ack packet = struct.pack('!HHB1013x', self.seqnum_server_packet, ack_num, ACK) self._lossy_layer.send_segment(packet) self.seqnum_server_packet += 1 else: print("Error: can't resolve packet") # Store all received data in data_buffer self.data_buffer += payload return True # Returns the next packet in the current window, using the FIFO principle def _next_packet(self): return self.receive_window.pop(0) # Returns the data_buffer def received_data(self): return self.data_buffer # Clean up any state def close(self): self._lossy_layer.destroy()
class BTCPClientSocket(BTCPSocket): def __init__(self, window, timeout): super().__init__(window, timeout) self._lossy_layer = LossyLayer(self, CLIENT_IP, CLIENT_PORT, SERVER_IP, SERVER_PORT) # Boolean variable that determines whether the server has a connection to the cient self._connected_to_server = False # Global variables that keep track of the sequence and the acknowledgement number that are send during # the three-way handshake self._x_value = 0 self._y_value = 0 # Variable that keeps track of the window size on the server side self._window_size_server = 0 # Selective repeater self._selective_repeater = SelectiveRepeaterSender(self._lossy_layer, self._window, self._timeout) # Called by the lossy layer from another thread whenever a segment arrives. def lossy_layer_input(self, rec_data): (header, packet) = rec_data seg_info = unpack_segment(header) (seq_number, ack_number, (ack, syn, fin), window, data_length, checksum, data) = seg_info if not syn and ack and not fin: if self._selective_repeater is not None: self._selective_repeater.ReceiveAckPacket(seg_info) else: print("Selective repeat protocol not initiated, but still receiving ACK") if syn and ack and not fin: self.acknowledge_server(seg_info) if not syn and ack and fin: # Terminate connection between client and server self._connected_to_server = False print("disconnected to server") # Perform a three-way handshake to establish a connection def connect(self): # Variable that keeps track of the moment when the first step of the three-way handshake was initiated. # Used to determine whether a connection timeout has occurred. connect_start = time.time() # Variable that keeps track of the number of attempts to connect to the server. # Used to determine whether the max number of connection attempts was exceeded. connect_attempts = 1 self.synchonize_server() print("connecting...") while not self._connected_to_server: if ((time.time() - connect_start) * 1000) >= self._timeout: # Connection Timeout if connect_attempts >= MAX_NUMBER_OF_CONNECTION_TRIES: # Max number of connection tries was exceeded print("Connection attempts failed: Max number of connection tries ({}) exceeded.".format(MAX_NUMBER_OF_CONNECTION_TRIES)) break else: #Try to connect again print("Connection attempt failed: Connection timeout, retrying...") connect_attempts += 1 connect_start = time.time() self.synchonize_server() # Step 3 of the three-way handshake to establish connection def synchonize_server(self): # Step 1 of three way handshake self._x_value = random.randint(0, MAX_VALUE_16_BIT_INTEGER + 1) ack_syn_fin = flags_to_binary(False, True, False) segment = Segment(self._x_value, 0, ack_syn_fin, 0, 0, 0, None) data = segment.create_segment() self._lossy_layer.send_segment(data) # Step 3 of the three-way handshake to establish connection def acknowledge_server(self, seg_info): # Step 3 of three way handshake (seq_number, ack_number, (ack, syn, fin), window, data_length, checksum, data) = seg_info if ack_number != self._x_value+1: print("acknowledgement number incorrect:", ack_number, self._x_value+1) else: self._y_value = seq_number self._window_size_server = window ack_syn_fin = flags_to_binary(True, False, False) segment = Segment(self._x_value+1, self._y_value+1, ack_syn_fin, 0, 0, 0, None) data = segment.create_segment() self._lossy_layer.send_segment(data) print("connected to server.") self._connected_to_server = True # Send data originating from the application in a reliable way to the server def send(self, data): self._selective_repeater.StartSending(data) # Perform a handshake to terminate a connection def disconnect(self): # Variable that keeps track of the moment when the first step of the termination handshake was initiated. # Used to determine whether a termination timeout has occurred. terminate_start = time.time() # Variable that keeps track of the number of attempts to terminate the connection to the server. # Used to determine whether the max number of termination attempts was exceeded. terminate_attempts = 1 self.finish_server() print("disconnecting...") while self._connected_to_server: if ((time.time() - terminate_start) * 1000) >= self._timeout: # Connection Timeout if terminate_attempts >= MAX_NUMBER_OF_TERMINATION_TRIES: # Max number of connection tries was exceeded print("Termination attempts failed: Max number of termination tries ({}) exceeded.".format( MAX_NUMBER_OF_TERMINATION_TRIES)) self._connected_to_server = False break else: # Try to connect again print("Termination attempt failed: Termination timeout, retrying...") terminate_attempts += 1 terminate_start = time.time() self.finish_server() # First step of termination handshake def finish_server(self): ack_syn_fin = flags_to_binary(False, False, True) segment = Segment(0, 0, ack_syn_fin, 0, 0, 0, None) data = segment.create_segment() self._lossy_layer.send_segment(data) def clean(self): self._connected_to_server = False self._x_value = 0 self._y_value = 0 self._window_size_server = 0 self._selective_repeater = SelectiveRepeaterSender(self._lossy_layer, self._window, self._timeout) # Clean up any state def close(self): self._lossy_layer.destroy() self._connected_to_server = False self._x_value = 0 self._y_value = 0 self._window_size_server = 0
class BTCPClientSocket(BTCPSocket): def __init__(self, window, timeout): super().__init__(window, timeout) self._lossy_layer = LossyLayer(self, CLIENT_IP, CLIENT_PORT, SERVER_IP, SERVER_PORT) self._btcpsocket = BTCPSocket self._HandshakeSuccessful = False self._Disconnected = False self._timeout = timeout self._First_Packet_Sequence_Number = 0 self._PacketList = [] self._AckPacket = None self._SequenceNumberList = [] self._Packetloss = 0 self._packets1 = [] self._retryCounter = 10 # Called by the lossy layer from another thread whenever a segment arrives. def lossy_layer_input(self, segment): packet_bytes, address = segment self._PacketList.append(packet_bytes) # Perform a three-way handshake to establish a connection def connect(self): print("Trying handshake (client-sided)") while self._retryCounter >= 0 and not self._HandshakeSuccessful: self._PacketList = [] header = Header( random.getrandbits(15) & 0xffff, 0, Flags(1, 0, 0), self._window, 0, 0 ) #We use 15 bits instead of 16 because there are cases that random.getrandbits() returns a large integer that will overflow the 16 bit length when we add sequence numbers to it and eventually results in an error (struct.error: ushort format requires 0 <= number <= 0xffff) Syn_packet = Packet(header, "") Syn_packet_bytes = Syn_packet.pack_packet() self._lossy_layer.send_segment(Syn_packet_bytes) if self.wait_for_packet(): Syn_Ack_Packet_bytes = self._PacketList[0] Syn_Ack_Packet = Packet.unpack_packet(Syn_Ack_Packet_bytes) self._First_Packet_Sequence_Number = Syn_Ack_Packet.getHeader( ).getAckNumber() Ack_number = Syn_Ack_Packet.getHeader().getSynNumber() + 1 if self._btcpsocket.CheckChecksum(Syn_Ack_Packet_bytes): Ack_header = Header(0, Ack_number, Flags(0, 1, 0), self._window, 0, 0) Ack_packet = Packet(Ack_header, "") self._AckPacket = Ack_packet Ack_packetBytes = Ack_packet.pack_packet() self._lossy_layer.send_segment(Ack_packetBytes) self._HandshakeSuccessful = True print("Client handshake successful") self._PacketList = [] else: print( "Retrying handshake, the checksum of the syn-ack packet is invalid" ) self._retryCounter -= 1 else: print("Retrying handshake, packet timeout") self._retryCounter -= 1 if self._retryCounter < 0: print("Number of tries exceed, quitting connection...") def wait_for_packet(self): timeNow = time.time() while self._PacketList == [] and time.time( ) < timeNow + self._timeout * 0.001: time.sleep(0.001) if not self._PacketList: return False else: return True # Send data originating from the application in a reliable way to the server def send(self, data): packets = [] content = open(data).read() sequence_number_updated = self._First_Packet_Sequence_Number # Making packet bytes from the input file while len(content) > 0: if len(content) >= 1008: header = Header(sequence_number_updated, 0, Flags(0, 0, 0), self._window, 1008, 0) packet = Packet(header, content[:1008]) packet_bytes = packet.pack_packet() content = content[1008:] self._SequenceNumberList.append(sequence_number_updated) sequence_number_updated += 1008 packets.append(packet_bytes) else: header = Header(sequence_number_updated, 0, Flags(0, 0, 0), self._window, len(content), 0) packet = Packet(header, content) packet_bytes = packet.pack_packet() packets.append(packet_bytes) self._SequenceNumberList.append(sequence_number_updated) sequence_number_updated += len(content) content = "" # Sending the packets to the client packet_length = len(packets) i = 0 k = self._window while packet_length > self._window: packet_length = packet_length - self._window for j in range(i, k): self.ResubmitPacket(packets[j]) i += self._window k += self._window for m in range(i, len(packets)): self.ResubmitPacket(packets[m]) # We implemented a stop-and-wait protocol, every packet we send to the server we wait until we receive the right ack # packet from the server, else we resend the packet def ResubmitPacket(self, packetbytes): self._PacketList = [] self._lossy_layer.send_segment(packetbytes) while not self.wait_for_packet(): self._lossy_layer.send_segment(packetbytes) packet = Packet.unpack_packet(packetbytes) while not self.CheckIfAckPacketAndCorrectNumber( packet.getHeader().getSynNumber() + packet.getHeader().getDatalength()): if Packet.unpack_packet(self._PacketList[0]).getHeader().getFlags( ).flag_to_int() == 6: self._lossy_layer.send_segment(self._AckPacket.pack_packet()) self._PacketList = [] while not self.wait_for_packet(): self._lossy_layer.send_segment( self._AckPacket.pack_packet()) else: self._lossy_layer.send_segment(packetbytes) self._PacketList = [] while not self.wait_for_packet(): self._lossy_layer.send_segment(packetbytes) # Check if the ack packet we receive from the server coincides with the data packet we just sent def CheckIfAckPacketAndCorrectNumber(self, SynNumber): if Packet.unpack_packet(self._PacketList[0]).getHeader().getFlags( ).flag_to_int() == 2 and Packet.unpack_packet( self._PacketList[0]).getHeader().getAckNumber() == SynNumber: return True for i in range(0, len(self._SequenceNumberList)): if Packet.unpack_packet(self._PacketList[0]).getHeader( ).getAckNumber() == self._SequenceNumberList[i]: self._Packetloss = i return False # Perform a handshake to terminate a connection def disconnect(self): print("Trying to disconnect (client-sided):") self._retryCounter = 10 while self._retryCounter >= 0 and not self._Disconnected: self._PacketList = [] header = Header( random.getrandbits(15) & 0xffff, 0, Flags(0, 0, 1), self._window, 0, 0 ) #We use 15 bits instead of 16 because there are cases that random.getrandbits() returns a large integer that will overflow the 16 bit length when we add sequence numbers to it and eventually results in an error (struct.error: ushort format requires 0 <= number <= 0xffff) Fin_packet = Packet(header, "fin") Fin_packet_bytes = Fin_packet.pack_packet() self._lossy_layer.send_segment(Fin_packet_bytes) if self.wait_for_packet(): if Packet.unpack_packet(self._PacketList[0]).getHeader( ).getFlags().flag_to_int() == 3: Fin_Ack_Packet_bytes = self._PacketList[0] self._PacketList.pop(0) Fin_Ack_Packet = Packet.unpack_packet(Fin_Ack_Packet_bytes) Ack_number = Fin_Ack_Packet.getHeader().getSynNumber() + 1 if self._btcpsocket.CheckChecksum(Fin_Ack_Packet_bytes): Ack_header = Header(0, Ack_number, Flags(0, 1, 0), self._window, 0, 0) Ack_packet = Packet(Ack_header, "ack") Ack_packet_bytes = Ack_packet.pack_packet() self._lossy_layer.send_segment(Ack_packet_bytes) self._Disconnected = True print("Client disconnection successful") else: print( "Retrying disconnection, the checksum of the fin-ack packet is invalid" ) self._retryCounter -= 1 else: print( "Retrying handshake, the received packet is not a fin-ack packet" ) self._retryCounter -= 1 else: print("Retrying handshake, packet timeout") self._retryCounter -= 1 if self._retryCounter < 0: print("Number of tries exceeded, going to disconnect") self._Disconnected = True # Clean up any state and close the socket def close(self): self._lossy_layer.destroy() pass
class BTCPServerSocket(BTCPSocket): def __init__(self, window, timeout): self._btcpsocket = BTCPSocket super().__init__(window, timeout) self._lossy_layer = LossyLayer(self, SERVER_IP, SERVER_PORT, CLIENT_IP, CLIENT_PORT) self._HandshakeSuccessful = False self._Disconnected = False self._PacketList = [] self._OutputList = [] self._PacketCounter = 0 self._AckNumber = 0 # Called by the lossy layer from another thread whenever a segment arrives def lossy_layer_input(self, segment): packet_bytes, address = segment self._PacketList.append(packet_bytes) self._ReceivedPacket = packet_bytes self._PacketCounter += 1 pass # Wait for the client to initiate a three-way handshake def accept(self): print("Trying handshake (server-sided)") while not self._HandshakeSuccessful: self._PacketList = [] self.wait_for_packet() # Wait for the Syn packet from the client Syn_packet_bytes = self._PacketList[0] Syn_packet = Packet.unpack_packet(Syn_packet_bytes) self._PacketList.pop(0) if Syn_packet.getHeader().getFlags().flag_to_int( ) == 4 and self._btcpsocket.CheckChecksum(Syn_packet_bytes): # If the received packet from the client is correct and is a syn packet, create a syn-ack packet and # send it to the client flags = Flags(1, 1, 0) ack_number = Syn_packet.getHeader().getSynNumber() + 1 header = Header( random.getrandbits(15) & 0xffff, ack_number, flags, self._window, 0, 0) Syn_Ack_packet = Packet(header, "") Syn_Ack_packet_bytes = Syn_Ack_packet.pack_packet() self._AckNumber = ack_number self._lossy_layer.send_segment(Syn_Ack_packet_bytes) self.wait_for_packet( ) # Wait for the Ack packet from the client Ack_packet_bytes = self._PacketList[0] Ack_packet = Packet.unpack_packet(Ack_packet_bytes) self._PacketList.pop(0) if Ack_packet.getHeader().getFlags().flag_to_int() == 2: if self._btcpsocket.CheckChecksum(Ack_packet_bytes): self._HandshakeSuccessful = True print("Server handshake successful.") else: print( "Ack packet checksum incorrect, retrying handshake..." ) else: print( "Received packet is not an Ack packet, resending the syn-ack packet..." ) while not self._HandshakeSuccessful: self._lossy_layer.send_segment(Syn_Ack_packet_bytes) self.wait_for_packet() packet = Packet.unpack_packet(self._PacketList[0]) self._PacketList.pop(0) if packet.getHeader().getFlags().flag_to_int() == 2: if self._btcpsocket.CheckChecksum( packet.pack_packet()): self._HandshakeSuccessful = True print("Server handshake successful") else: print( "Retrying handshake, the checksum of the packet is invalid or the packet is not an ack packet" ) # Wait until a packet is received from the client def wait_for_packet(self): while not self._PacketList: time.sleep(0.001) # Send any incoming data to the application layer def recv(self): self.wait_for_packet() while not Packet.unpack_packet( self._PacketList[0]).getHeader().getFlags().flag_to_int() == 1: packet = Packet.unpack_packet(self._PacketList[0]) if packet.getHeader().getSynNumber() == self._AckNumber: self._AckNumber += packet.getHeader().getDatalength() header = Header(0, self._AckNumber, Flags(0, 1, 0), self._window, 0, 0) AckPacket = Packet(header, "") AckPacket_bytes = AckPacket.pack_packet() self._lossy_layer.send_segment(AckPacket_bytes) self._OutputList.append(self._PacketList[0]) self._PacketList.pop(0) self.wait_for_packet() else: header = Header(0, self._AckNumber, Flags(0, 1, 0), self._window, 0, 0) AckPacket = Packet(header, "") AckPacket_bytes = AckPacket.pack_packet() self._lossy_layer.send_segment(AckPacket_bytes) self._PacketList.pop(0) self.wait_for_packet() # Perform a handshake to terminate a connection def disconnect(self): print("Trying to disconnect (server-sided):") while not self._Disconnected: Fin_packet_bytes = self._PacketList[0] self._PacketList.pop(0) if Packet.unpack_packet(Fin_packet_bytes).getHeader().getFlags( ).flag_to_int() == 1 and self._btcpsocket.CheckChecksum( Fin_packet_bytes): flags = Flags(0, 1, 1) Fin_packet = Packet.unpack_packet(Fin_packet_bytes) ack_number = Fin_packet.getHeader().getSynNumber() + 1 header = Header( random.getrandbits(15) & 0xffff, ack_number, flags, self._window, 0, 0) Fin_Ack_packet = Packet(header, "fin_ack") self._lossy_layer.send_segment(Fin_Ack_packet.pack_packet()) self._Disconnected = True print("Server disconnection successful") else: print( "Retrying disconnection, the checksum of the packet is invalid or the packet is not a fin packet" ) self.wait_for_packet() # Write the content of the received packets to a specified file (default output.file) def packet_to_file(self, file): content = "" for i in range(0, len(self._OutputList)): content += Packet.unpack_packet( self._OutputList[i]).getPayload().decode() f = open(file, 'w') f.write(content) f.flush() f.close() # Clean up any state and close the socket def close(self): self._lossy_layer.destroy()
class BTCPClientSocket(BTCPSocket): def __init__(self, window, timeout, printSegments, maxRetries): super().__init__(window, timeout) self._lossy_layer = LossyLayer(self, CLIENT_IP, CLIENT_PORT, SERVER_IP, SERVER_PORT) self._printSegments = printSegments self._currentState = "disconnected" self._maxRetries = maxRetries self._numberOfRetries = 0 self._nextSeqNum = 0 self._ackNum = 0 self._sendBase = 0 self._initialSequenceNumber = 0 self._timer = 0 self._timerLock = threading.Lock() self._disconnected = threading.Event() self._connected = threading.Event() self._entireFileAcknowledged = threading.Event() # Called by the lossy layer from another thread whenever a segment arrives. def lossy_layer_input(self, segment): if self._printSegments: print("Client has received: ") super().print_segment(segment[0]) seqnum, acknum, ACK, SYN, FIN, windowsize, cksumHasSucceeded, data = super( ).breakdown_segment(segment[0]) if (not cksumHasSucceeded): return self._window = windowsize #to make sure we do not have a window size of 0 if (self._window == 0): self._window = 1 if (self._currentState == "connecting"): if (SYN and (not FIN) and ACK): self._currentState = "connected" self._nextSeqNum = acknum self._ackNum = seqnum + 1 self._sendBase = acknum self.stopTimer() self._connected.set() elif (self._currentState == "connected"): if ((not SYN) and (not FIN) and ACK): #an ACK has been received if (acknum == self._lastSegment): self._entireFileAcknowledged.set() self.stopTimer() self._sendBase = acknum + 1 elif (acknum >= self._sendBase): self._sendBase = acknum + 1 self.startTimer() self.sendSegmentsInWindow() elif (self._currentState == "disconnecting"): if ((not SYN) and FIN and ACK): self.stopTimer() self._disconnected.set() # Perform a three-way handshake to establish a connection def connect(self): randomSeqNum = random.randint( 0, MAX_16BITS ) # Creating a random 16 bit value for the sequence number self._nextSeqNum = randomSeqNum self.sendSegment(randomSeqNum, 0, SYN=True) self._currentState = "connecting" self._numberOfRetries = 1 self.startTimer() self._connected.wait( ) # wait until the connection is established to return to the app return self._currentState == "connected" # return if the connection establishment succeeded def sendSegment(self, seqnum=0, acknum=0, ACK=False, SYN=False, FIN=False, windowsize=0, data: bytes = b''): newsegment = self.buildsegment(seqnum, acknum, ACK=ACK, SYN=SYN, FIN=FIN, windowsize=windowsize, data=data) if self._printSegments: print("The client has sent a segment with SEQ ", seqnum) self._lossy_layer.send_segment(newsegment) # Send data originating from the application in a reliable way to the server def send(self, data): self._sendPackets = [] for i in range(0, len(data), 1008): #splits it in parts of 1008 bytes self._sendPackets.append(data[i:i + 1008]) self._lastSegment = self._sendBase + len(self._sendPackets) - 1 self._initialSequenceNumber = self._sendBase self.startTimer() self.sendSegmentsInWindow() self._entireFileAcknowledged.wait() def startTimer(self): self.stopTimer( ) # in case a different timer is running currently, cancel it self._timerLock.acquire() self._timer = threading.Timer(self._timeout / 1000, self.timeout) self._timer.start() self._timerLock.release() def stopTimer(self): if (self._timer == 0): # timer has not yet been initialized return self._timerLock.acquire() self._timer.cancel() self._timerLock.release() def sendSegmentsInWindow(self): while (self._nextSeqNum <= self._sendBase + self._window and self._nextSeqNum <= self._lastSegment): if (self._nextSeqNum == self._initialSequenceNumber): self.sendSegment( self._nextSeqNum, self._ackNum, ACK=True, data=self._sendPackets[self._nextSeqNum - self._initialSequenceNumber]) else: self.sendSegment( self._nextSeqNum, data=self._sendPackets[self._nextSeqNum - self._initialSequenceNumber]) self._nextSeqNum += 1 def timeout(self): if (self._currentState == "connecting"): if (self._numberOfRetries > self._maxRetries): self._connected.set() return self._numberOfRetries += 1 randomSeqNum = random.randint( 0, MAX_16BITS ) # Creating a random 16 bit value for the sequence number self._nextSeqNum = randomSeqNum self.sendSegment(randomSeqNum, 0, SYN=True) elif (self._currentState == "connected"): if (self._printSegments): print("a timeout has occured, we restart with seqnum : ", self._sendBase, ', which is package number ', self._sendBase - self._initialSequenceNumber) self._nextSeqNum = self._sendBase self.sendSegmentsInWindow() elif (self._currentState == "disconnecting"): if (self._numberOfRetries > self._maxRetries): self._disconnected.set() self._numberOfRetries += 1 self.sendSegment(self._nextSeqNum, FIN=True) self.startTimer() # Perform a handshake to terminate a connection def disconnect(self): self._currentState = "disconnecting" self._numberOfRetries = 1 self.startTimer() self.sendSegment(self._nextSeqNum, FIN=True) self._disconnected.wait() self._currentState = "disconnected" # Clean up any state def close(self): self._lossy_layer.destroy()
class BTCPServerSocket(BTCPSocket): def __init__(self, window, timeout): super().__init__(window, timeout) self._lossy_layer = LossyLayer(self, SERVER_IP, SERVER_PORT, CLIENT_IP, CLIENT_PORT) # Boolean variable that determines whether the server has a connection to the client self._connected_to_client = False # Boolean variable that is true when the server is listening for the clients initiation of the three-way handshake self._listening = False # Global variables that keep track of the sequence and the acknowledgement number that are send during # the three-way handshake self._x_value = 0 self._y_value = 0 # Selective repeater self._selective_repeater = SelectiveRepeaterReceiver(self._lossy_layer, self._window) # Called by the lossy layer from another thread whenever a segment arrives def lossy_layer_input(self, rec_data): (header, packet) = rec_data seg_info = unpack_segment(header) (seq_number, ack_number, (ack, syn, fin), window, data_length, checksum, data) = seg_info if syn and not ack and not fin: if self._listening: self.accept_syn_packet(seg_info) if not syn and ack and not fin: if ack_number != self._y_value+1: print("acknowledgement number incorrect:", ack_number, self._y_value+1) else: print("connected to client.") self._connected_to_client = True self._listening = False if not syn and not ack and fin: self.finish_client() if not syn and not ack and not fin: self._selective_repeater.ReceivePacket(seg_info) # Wait for the client to initiate a three-way handshake def accept(self): self._listening = True # accept a packet from the client with the syn flag and do the server part of the three-way handshake def accept_syn_packet(self, seg_info): (seq_number, ack_number, (ack, syn, fin), window, data_length, checksum, data) = seg_info self._x_value = seq_number self._y_value = random.randint(0, MAX_VALUE_16_BIT_INTEGER + 1) ack_syn_fin = flags_to_binary(True, True, False) segment = Segment(self._y_value, self._x_value + 1, ack_syn_fin, self._window, 0, 0, None) data = segment.create_segment() self._lossy_layer.send_segment(data) # Second step of termination handshake def finish_client(self): ack_syn_fin = flags_to_binary(True, False, True) segment = Segment(0, 0, ack_syn_fin, 0, 0, 0, None) data = segment.create_segment() self._lossy_layer.send_segment(data) self._connected_to_client = False print("disconnected to client") # Send any incoming data to the application layer def recv(self, output): data = bytes() collected_data = bytes() # Wait for data to be delivered while len(data) == 0: data = self._selective_repeater.DeliverData() # Write bytes to file with path "output" f = open(output, 'wb') f.write(data) f.close() print("Data Received") def clean(self): self._listening = False self._x_value = 0 self._y_value = 0 self._selective_repeater = SelectiveRepeaterReceiver(self._lossy_layer, self._window) # Clean up any state def close(self): self._listening = False self._lossy_layer.destroy() self._x_value = 0 self._y_value = 0
class BTCPClientSocket: def __init__(self, timeout): self._lossy_layer = LossyLayer(self, CLIENT_IP, CLIENT_PORT, SERVER_IP, SERVER_PORT) self._timeout = timeout / 100000 # The timeout for one segment in seconds. self._timer = None # The timer used to detect a timeout. self._seq_num = None # The initial sequence number, initialized during establishment. # Variables for sending data over to the server. self._send_flag = None # A flag to signify when to stop all the threads. self._seg_tries = None # The amount of tries every segment gets. self._segments = None # All segments which are to be send plus the amount of tries left for every segment. self._status = None # The status for every segment: 0 not send, 1 send not ACKed, 2 timeout, 3 ACKed. self._pending = None # The time at which certain segments are send, contains (seq_num, time send). self._acks = None # All of the acknowledgements which need to be processed. self._send_base = None # The index for self._segments up to which all segments are ACKed. self._window_size = None # The window size of the server, initialized during establishment. self._status_lock = None # Ensure that changing the status list is done thread safe. self._pending_lock = None # Ensure that changing the pending list is done thread safe. self._send_base_lock = None # Ensure that changing the send base is done thread safe. # Variables for the connection establishment phase. self._syn_tries = None # The number of tries to establish a connection. self._connected = None # A boolean to signify if the connection was successful, returned by connect(). self._connected_flag = None # An event to signify when the connection is established. # Variables for the connection termination phase. self._fin_tries = None # The number of tries to terminate a connection. self._finished = None # A boolean to signify if the closing was (ab)normal, returned by disconnect(). self._finished_flag = None # An event to signify when the connection is terminated. # Called by the lossy layer from another thread whenever a segment arrives. def lossy_layer_input(self, segment): try: seq_num, ack_num, flags, window_size, data = bytes_to_ascii( segment) if flags[0] and flags[1]: # ACK & SYN self._handle_syn(seq_num, ack_num, window_size) elif flags[0] and flags[2]: # ACK & FIN self._handle_fin() elif flags[0]: # ACK self._handle_ack(ack_num, window_size) except ValueError: # Incorrect checksum or data length. pass # Perform a three-way handshake to establish a connection. def connect(self): # Create the initial sequence number and amount of tries to establish a connection and the connected flag. self._connected_flag = threading.Event() self._connected = False self._syn_tries = 30 self._seq_num = random.randint(0, 0xffff) # Send the first segment to the server. segment = ascii_to_bytes(self._seq_num, 0, [False, True, False], 0, b'') self._lossy_layer.send_segment(segment) # Create and start a timer for the connection establishment phase. self._timer = threading.Timer(self._timeout, self._handle_syn_timeout) self._timer.start() # Wait until the connection handshake is done. self._connected_flag.wait() return self._connected # Send data originating from the application in a reliable way to the server. def send(self, data): data = data.encode() # Initialize all the variables. self._send_flag = threading.Event() self._seg_tries = 30 self._send_base = 0 self._segments = [[segment, self._seg_tries] for segment in create_segments(data, self._seq_num)] self._status = [0] * len(self._segments) self._pending = [] self._acks = [] self._status_lock = threading.Lock() self._pending_lock = threading.Lock() self._send_base_lock = threading.Lock() # Start the acknowledgement loop in a new thread. acks = threading.Thread(target=self._ack_loop) acks.start() # Start the timer in a new thread. timer = threading.Thread(target=self._timer_loop) timer.start() # Send all of the segments, returns if all are send. success = self._send_loop() # Set the _send_flag so the timer will stop. self._send_flag.set() acks.join() timer.join() # Return if the sending of all the segments was successful. return success # Perform a handshake to terminate a connection. def disconnect(self): self._finished_flag = threading.Event() self._finished = False self._fin_tries = 15 # Send a FIN to the server. segment = ascii_to_bytes(0, 0, [False, False, True], 0, b'') self._lossy_layer.send_segment(segment) # Create and start a timer for the connection termination phase. self._timer = threading.Timer(self._timeout, self._handle_fin_timeout) self._timer.start() # Wait until the termination handshake is done. self._finished_flag.wait() return self._finished # Clean up any state. def close(self): self._lossy_layer.destroy() def _handle_syn(self, seq_num, ack_num, window_size): if ack_num == self._seq_num + 1: self._timer.cancel() self._window_size = window_size self._seq_num += 1 # Send an ACK back to the server. segment = ascii_to_bytes(self._seq_num, seq_num + 1, [True, False, False], 0, b'') self._lossy_layer.send_segment(segment) # Signal the connect() function that the connection is established. self._connected = True self._connected_flag.set() def _handle_syn_timeout(self): if self._syn_tries <= 0: # Signal the connect() function that the connection could not be established. self._connected_flag.set() else: self._syn_tries -= 1 # Resend the initial segment. segment = ascii_to_bytes(self._seq_num, 0, [False, True, False], 0, b'') self._lossy_layer.send_segment(segment) # Restart the timeout timer. self._timer = threading.Timer(self._timeout, self._handle_syn_timeout) self._timer.start() def _handle_fin(self): self._timer.cancel() self._finished = True self._finished_flag.set() def _handle_fin_timeout(self): if self._fin_tries <= 0: # Signal the disconnect() function that the connection could not be normally terminated. self._finished_flag.set() else: self._fin_tries -= 1 # Resend the initial segment. segment = ascii_to_bytes(0, 0, [False, False, True], 0, b'') self._lossy_layer.send_segment(segment) # Restart the timeout timer. self._timer = threading.Timer(self._timeout, self._handle_fin_timeout) self._timer.start() def _handle_ack(self, ack_num, window_size): self._acks.append((ack_num, window_size)) def _ack_loop(self): while not self._send_flag.is_set(): if self._acks: ack_num, window_size = self._acks.pop(0) # Update the window size and increase the send base by one if this was the next to be ACKed segment. self._window_size = window_size try: self._send_base_lock.acquire() if self._send_base == ack_num - self._seq_num: self._send_base = ack_num - self._seq_num + 1 finally: self._send_base_lock.release() # Change the segment status to received ACK. try: self._status_lock.acquire() self._status[ack_num - self._seq_num] = 3 # ACKed flag finally: self._status_lock.release() # Remove the ACKed segment from the pending list. try: self._pending_lock.acquire() self._pending = [(seq_num, _time) for (seq_num, _time) in self._pending if seq_num != ack_num] finally: self._pending_lock.release() def _timer_loop(self): while not self._send_flag.is_set(): try: self._status_lock.acquire() self._pending_lock.acquire() still_pending = [] for (seq_num, time_send) in self._pending: if time.time_ns( ) // 1000 - time_send > self._timeout * 100000: # A timeout occurred, change the status flag to timeout so it will be resend. if self._status[self._seq_num - seq_num] != 3: # not ACKed self._status[self._seq_num - seq_num] = 2 # timeout flag else: still_pending.append((seq_num, time_send)) self._pending = still_pending finally: self._pending_lock.release() self._status_lock.release() time.sleep(0.005) def _send_loop(self): while self._send_base < len( self._segments): # There are segments left to get ACKed. # Check all the segments from the send base till the window if one can be send and then send ONE or none. try: self._send_base_lock.acquire() self._status_lock.acquire() for index, status in enumerate( self._status[self._send_base:self._send_base + self._window_size]): if (status == 0 or status == 2) and len( self._pending ) < self._window_size: # not send or timeout # Check if the amount of tries for this segment is exceeded. if self._segments[self._send_base + index][1] <= 0: return False else: self._segments[self._send_base + index][1] -= 1 # Send the segment, add it to the pending segments and update the status. self._lossy_layer.send_segment( self._segments[self._send_base + index][0]) self._status[self._send_base + index] = 1 # send but not ACKed flag try: self._pending_lock.acquire() self._pending.append( (self._send_base + index + self._seq_num, time.time_ns() // 1000)) finally: self._pending_lock.release() finally: self._status_lock.release() self._send_base_lock.release() return True
class BTCPServerSocket(BTCPSocket): def __init__(self, window, timeout): super().__init__(window, timeout) self.window = window self.timeout = timeout self.thread_executor = ThreadPoolExecutor(max_workers=window) self._lossy_layer = LossyLayer(self, SERVER_IP, SERVER_PORT, CLIENT_IP, CLIENT_PORT) self.state = State.CLOSED # Called by the lossy layer from another thread whenever a segment arrives def lossy_layer_input(self, segment): segment = btcp.packet.unpack_from_socket(segment) if not segment.confirm_checksum() or self.state == State.CLOSED: # discard segment pass if segment.packet_type() == "SYN": self.state = State.SYN_RECVD self.thread_executor.submit(self.handshake_response_thread, segment) elif segment.packet_type() == "ACK": self.state = State.HNDSH_COMP elif segment.packet_type() == "FIN": self.state = State.FIN_RECVD self.close_connection() self.close() else: pass # Wait for the client to initiate a three-way handshake def accept(self): self.state = State.LISTEN # Send any incoming data to the application layer def recv(self): pass # Clean up any state def close(self): self.thread_executor.shutdown(wait=True) print("DESTROY SERVER SOCKET") self._lossy_layer.destroy() # def handshake_response_thread(self, segment): seq_nr = randint(0, 65535) - segment.get_seq_nr() ack_nr = segment.get_seq_nr() + 1 segment.up_seq_nr(seq_nr) segment.up_ack_nr(ack_nr) segment.set_flags(True, True, False) self._lossy_layer.send_segment(segment.pack()) self.state = State.SYN_SEND while True: # as long as no ACK handshake segment is received time.sleep(self.timeout / 1000) if (self.state != State.HNDSH_COMP): self._lossy_layer.send_segment(segment.pack()) else: break # def close_connection(self): segment = TCPpacket() segment.set_flags(True, False, True) send_segment = segment.pack() self._lossy_layer.send_segment(send_segment)