def connect(self, dst_adr):
        self.destination = dst_adr
        syn_ack_received = False
        self.io.start()

        # send SYN
        self.logger.debug('Sending SYN to initiate handshake.')
        syn_packet = RxPPacket(
            self.port_number,
            self.destination[1],
            seq_number=self.seq_number,
            syn=True
        )
        self.io.send_queue.put((syn_packet, self.destination))
        self.seq_number += 1
        ack_sent = False
        ack_confirmed = False
        syn_ack_timeout = 1

        # wait for SYN/ACK
        while not (syn_ack_received and ack_confirmed):
            syn_ack_packet = None
            try:
                syn_ack_packet, address = self.io.recv_queue.get(True, syn_ack_timeout)
            except Queue.Empty:
                if syn_ack_packet == None and ack_sent:
                    ack_confirmed = True
                    break

                self.logger.debug('Timed out waiting on SYN/ACK during handshake; retransmitting SYN.')
                syn_packet.frequency += 1
                syn_packet.update_checksum()
                self.io.send_queue.put((syn_packet, self.destination))
                continue

            if syn_ack_packet and address:
                syn_ack_received = self.__verify_syn_ack(syn_ack_packet, address, syn_packet.seq_number)

                if syn_ack_received:
                    self.logger.debug('Received SYN/ACK during handshake; sending ACK to finish handshake.')
                    ack_sent = True
                    syn_ack_timeout = 4
                    ack_packet = RxPPacket(
                        self.port_number,
                        self.destination[1],
                        seq_number=self.seq_number,
                        ack_number=syn_ack_packet.seq_number + 1,
                        ack=True
                    )
                    self.io.send_queue.put((ack_packet, self.destination))

        self.seq_number += 1
        self.cxn_status = RxPConnectionStatus.IDLE
    def accept(self):
        syn_received = False
        ack_received = False

        # NOTE: starts thread's activity. arranges for the run() method to be invoked on a separate thread of control
        if not self.io.isAlive():
            self.io.start()

        # wait for a syn that passes verification
        while not syn_received:
            try:
                # NOTE: 1st param blocks, 2nd is timeout (on queue.get)
                syn_packet, self.destination = self.io.recv_queue.get(True, 1)
            except Queue.Empty:
                continue

            if syn_packet and self.destination:
                syn_received = self.__verify_syn(syn_packet, self.destination)

        # send SYN/ACK
        self.logger.debug('Received SYN during handshake; sending SYN/ACK to progress handshake.')
        syn_ack_packet = RxPPacket(
            self.port_number,
            self.destination[1],
            ack_number=syn_packet.seq_number + 1,  # use SYN recvd seq number for ACK
            ack=True,
            syn=True
        )

        self.io.send_queue.put((syn_ack_packet, self.destination))
        self.seq_number += 1

        # wait for ACK from client confirming SYN/ACK received
        while not ack_received:
            try:
                ack_packet, address = self.io.recv_queue.get(True, 1)
            except Queue.Empty:
                self.logger.debug('Timed out waiting on ack during handshake; retransmitting SYN/ACK.')
                syn_ack_packet.frequency += 1
                syn_ack_packet.update_checksum()
                self.io.send_queue.put((syn_ack_packet, self.destination))
                continue

            if ack_packet and address:
                ack_received = self.__verify_ack(ack_packet, address, syn_ack_packet.seq_number)
                if ack_received:
                    self.logger.debug('Received ACK during handshake; client socket accepted')

        self.cxn_status = RxPConnectionStatus.IDLE
    def run(self):
        while True:
            try:
                packet, address = self.send_queue.get(True, 0.1)
                self.socket.sendto(packet.serialize(), address)
            except Queue.Empty:
                pass

            try:
                packet, address = self.socket.recvfrom(4096)
                packet = RxPPacket.parse(packet)
                self.recv_queue.put((packet, address))
            except (socket.timeout, ParseException), e:
                pass
    def send(self, msg):
        floor = 0
        payload_size = 512
        packets = []

        # chunk data into packets
        while floor < len(msg):
            if floor + payload_size <= len(msg):
                payload = msg[floor: floor + payload_size]
            else:
                payload = msg[floor:]

            data_packet = RxPPacket(
                self.port_number,
                self.destination[1],
                seq_number=self.seq_number,
                payload=payload
            )
            packets.append(data_packet)
            self.seq_number += 1
            floor += payload_size

        kill_packet = RxPPacket(
            self.port_number,
            self.destination[1],
            seq_number=self.seq_number
        )
        kills_sent = 0  # allow to be sent 3 times before dropping client cxn
        kill_seq_number = kill_packet.seq_number
        packets.append(kill_packet)  # put that shit at the end after we've added all the other packets
        self.seq_number += 1

        self.logger.debug('Placing packets in window to be sent now...')
        window = SlidingWindow(packets, self.window_size)
        time_sent = time.time()
        time_remaining = self.retransmit_timer.timeout

        self.cxn_status = RxPConnectionStatus.SEND
        for data_packet in window.window:
            self.io.send_queue.put((data_packet, self.destination))

        while not window.is_empty():
            try:
                ack_packet, address = self.io.recv_queue.get(True, time_remaining)

            # resend entire send window (all packets that weren't yet ACK'd by the receiver)
            except Queue.Empty:
                self.logger.debug(
                    'Timed out waiting for ack during data transmission; retransmitting unacknowledged packets.')
                time_sent = time.time()
                time_remaining = self.retransmit_timer.timeout

                for data_packet in window.window:
                    if kill_seq_number == data_packet.seq_number:
                        kills_sent += 1
                        if kills_sent > 3:  # if retransmitted 3 times already, kill cxn with client
                            self.logger.debug(
                                'Kill packet failed to be acknowledged; unable to end connection, closing now.')
                            return

                    if not window.acknowledged_packets.get(data_packet.seq_number):
                        data_packet.frequency += 1
                        data_packet.update_checksum()
                        self.io.send_queue.put((data_packet, self.destination))
                continue

            # if still getting SYN/ACK, retransmit ACK
            if self.__verify_syn_ack(ack_packet, address, 1):
                self.logger.debug('Received SYN/ACK retransmission; retransmitting ACK.')
                ack_packet = RxPPacket(
                    self.port_number,
                    self.destination[1],
                    ack_number=ack_packet.seq_number + 1,
                    seq_number=2,
                    ack=True
                )
                self.io.send_queue.put((ack_packet, self.destination))
                time_remaining = 0

            # if a packet in window is acknowledged, slide the window past said received packet
            elif self.__verify_is_ack(ack_packet, address) and window.index_of_packet(ack_packet) >= 0:
                self.retransmit_timer.update(ack_packet.frequency, time.time() - time_sent)
                self.logger.debug(
                    'ACK received. Updated retransmit timer; timeout is now ' + str(self.retransmit_timer.timeout))
                window.acknowledge_packet(ack_packet)

                if self.__verify_ack(ack_packet, address, window.window[0].seq_number):
                    additions = window.slide()
                    # send newly added packets if they were added
                    if additions > 0:
                        while additions > 0:
                            self.io.send_queue.put((window.window[-additions], self.destination))
                            self.seq_number += 1
                            additions -= 1

            # otherwise, update time remaining
            else:
                time_remaining -= time.time() - time_sent / 4  # decay
                if time_remaining < time.time():
                    time_remaining = .5
                self.logger.debug('Trash packet receive; time remaining before next timeout: ' + str(time_remaining))

        self.cxn_status = RxPConnectionStatus.IDLE