示例#1
1
class PTCProtocol(object):

    def __init__(self, alpha, beta, delay, dropChance, wait):
        self.state = CLOSED
        self.control_block = None
        self.packet_builder = PacketBuilder()
        self.socket = Soquete()
        self.rcv_wnd = RECEIVE_BUFFER_SIZE
        self.iss = self.compute_iss()
        self.rqueue = RetransmissionQueue()
        self.read_stream_open = True
        self.write_stream_open = True
        self.rto_estimator = RTOEstimator(self, alpha, beta, delay, dropChance)
        self.packet_handler = IncomingPacketHandler(self)
        self.delay = delay
        self.dropChance = dropChance
        self.wait = wait
        self.ticks = 0
        self.retransmissions = 0
        self.close_mode = NO_WAIT
        self.close_event = threading.Event()
        self.initialize_threads()
        self.initialize_timers()

    def initialize_threads(self):
        self.packet_sender = PacketSender(self)
        self.packet_receiver = PacketReceiver(self)
        self.clock = Clock(self)

    def initialize_timers(self):
        self.retransmission_timer = RetransmissionTimer(self)

    def start_threads(self):
        self.packet_receiver.start()
        self.packet_sender.start()
        self.clock.start()

    def stop_threads(self):
        self.packet_receiver.stop()
        self.packet_sender.stop()
        self.packet_sender.notify()
        self.clock.stop()

    def join_threads(self):
        self.packet_receiver.join()
        self.packet_sender.join()
        self.clock.join()

    def set_state(self, state):
        self.state = state
        if state == CLOSED or\
           (self.close_mode == NO_WAIT and state == FIN_WAIT2):
            # Señalizar este evento cuando la conexión queda completamente
            # cerrada o bien cuando el usuario del socket explícitamente
            # eligió esperar a que el interlocutor también cierre. Por
            # defecto, el comportamiento es similar al de TCP (i.e., cierre
            # asimétrico).
            self.close_event.set()
        if state == ESTABLISHED:
            self.connected_event.set()

    def compute_iss(self):
        value = random.randint(0, MAX_SEQ/2)
        return SequenceNumber(value)

    def initialize_control_block_from(self, packet):
        # +1 dado que el SYN se secuencia.
        receive_seq = 1 + packet.get_seq_number()
        send_seq = 1 + self.iss
        send_window = packet.get_window_size()
        receive_window = self.rcv_wnd
        self.control_block = PTCControlBlock(send_seq, receive_seq,
                                             send_window, receive_window)

    def is_connected(self):
        connected_states = [ESTABLISHED, FIN_WAIT1, FIN_WAIT2, CLOSE_WAIT,
                            CLOSING, LAST_ACK]
        return self.state in connected_states

    def build_packet(self, seq=None, ack=None, payload=None, flags=None,
                     window=None):
        if seq is None:
            seq = self.control_block.get_snd_nxt()
        if flags is None:
            flags = [ACKFlag]
        if ack is None and ACKFlag in flags:
            ack = self.control_block.get_rcv_nxt()
        if window is None:
            window = self.control_block.get_rcv_wnd()
        packet = self.packet_builder.build(payload=payload, flags=flags,
                                           seq=seq, ack=ack, window=window)
        return packet

    def send_and_queue(self, packet, is_retransmission=False):
        if is_retransmission:
            # Algoritmo de Karn: no usar paquetes retransmitidos para
            # actualizar las estimaciones del RTO.
            if self.rto_estimator.is_tracking_packets():
                tracked_packet = self.rto_estimator.get_tracked_packet()
                tracked_seq = tracked_packet.get_seq_number()
                if tracked_seq == packet.get_seq_number():
                    self.rto_estimator.untrack()
        else:
            # Sólo se hará seguimiento de paquetes frescos para estimar el
            # RTT (otra vez por el algoritmo de Karn).
            if not self.rto_estimator.is_tracking_packets():
                self.rto_estimator.track(packet)
            # Encolar este paquete fresco para eventuales retransmisiones.
            # Las retransmisiones no se reencolan pues quedan al principio de
            # la cola hasta que son correctamente reconocidas.
            self.rqueue.put(packet)

        if not self.retransmission_timer.is_running():
            # Usar la estimación actual del RTO para medir este paquete.
            current_rto = self.rto_estimator.get_current_rto()
            self.retransmission_timer.start(current_rto)

        if self.wait and self.state == ESTABLISHED:
            if random.randint(0, 100) >= self.dropChance:
                time.sleep(self.delay * CLOCK_TICK)
                self.socket.send(packet)
            else:
                print '#DROP!'
        else:
            self.socket.send(packet)

    def set_destination_on_packet_builder(self, address, port):
        self.packet_builder.set_destination_address(address)
        self.packet_builder.set_destination_port(port)

    def bind(self, address, port):
        self.socket.bind(address, port)
        self.packet_builder.set_source_address(address)
        self.packet_builder.set_source_port(port)

    def listen(self):
        self.set_state(LISTEN)

    def connect_to(self, address, port):
        self.connected_event = threading.Event()
        self.set_destination_on_packet_builder(address, port)
        self.start_threads()

        syn_packet = self.build_packet(seq=self.iss, flags=[SYNFlag],
                                       window=self.rcv_wnd)
        self.set_state(SYN_SENT)
        self.send_and_queue(syn_packet)

        self.connected_event.wait()

    def accept(self):
        if self.state != LISTEN:
            raise PTCError('should listen first')
        self.connected_event = threading.Event()
        self.start_threads()
        # Esperar hasta que un cliente desee conectarse.
        self.connected_event.wait()

    def send(self, data):
        with self.control_block:
            if not self.write_stream_open:
                raise PTCError('write stream is closed')
            self.control_block.to_out_buffer(data)
            self.packet_sender.notify()

    def receive(self, size):
        data = self.control_block.from_in_buffer(size)
        updated_rcv_wnd = self.control_block.get_rcv_wnd()
        if updated_rcv_wnd > 0:
            wnd_packet = self.build_packet(window=updated_rcv_wnd)

            if self.wait:
                time.sleep(self.delay * CLOCK_TICK)

            self.socket.send(wnd_packet)
        return data

    def get_ticks(self):
        return self.ticks

    def tick(self):
        self.ticks += 1
        self.retransmission_timer.tick()

    def acknowledge_packets_and_update_timers_with(self, packet):
        ack_number = packet.get_ack_number()
        if self.control_block.ack_is_accepted(ack_number):
            # Primero, pasar este paquete al estimador de RTO para que pueda
            # actualizar sus valores si corresponde (esto es, si el paquete
            # está reconociendo el paquete al que está haciendo seguimiento).
            self.rto_estimator.process_ack(packet)
            # Luego, desencolar paquetes para retransmitir y parar/reiniciar
            # el timer de retransmisiones según corresponda.
            self.remove_from_retransmission_queue_packets_acked_by(packet)

    def remove_from_retransmission_queue_packets_acked_by(self, packet):
        # Sólo ACKs mayores que SND_UNA y menores que SND_NXT son válidos
        # en este punto.
        with self.rqueue:
            snd_una = self.control_block.get_snd_una()
            snd_nxt = self.control_block.get_snd_nxt()
            # Ver qué paquetes encolados son totalmente reconocidos por
            # este paquete. Necesitamos SND_UNA y SND_NXT para poder
            # comparar correctamente los SEQs y ACKs.
            removed_packets = self.rqueue.remove_acknowledged_by(packet,
                                                                 snd_una,
                                                                 snd_nxt)

            if len(removed_packets) > 0:
                # Algunos paquetes se reconocieron, por lo que debemos
                # actualizar el timer de retransmisiones.
                self.adjust_retransmission_timer()

    def adjust_retransmission_timer(self):
        # Comenzar con cero retransmisiones para el próximo paquete.
        self.retransmissions = 0
        if self.rqueue.empty():
            # Todos los datos salientes quedaron reconocidos. Detener el timer.
            self.retransmission_timer.stop()
        else:
            # Algunos datos se reconocieron pero otros aún quedan.
            # Reiniciar el timer usando la estimación actual del RTO.
            current_rto = self.rto_estimator.get_current_rto()
            self.retransmission_timer.restart(current_rto)

    def handle_outgoing(self):
        if self.control_block is None:
            # Cuando la conexión todavía no fue establecida, no tenemos nada
            # para enviar.
            return
        with self.control_block:
            # Analizar primero si tenemos un timeout de un paquete enviado.
            if self.retransmission_timer.has_expired() and\
               not self.rqueue.empty():
                # Si alcanzamos el máximo número permitido de retransmisiones,
                # liberar la conexión.
                if self.retransmissions >= MAX_RETRANSMISSION_ATTEMPTS:
                    self.free()
                    return
                # Limpiar la estimación del RTT si se hizo back-off reiteradas
                # veces. Posiblemente ya no represente el RTT real.
                if self.retransmissions > BOGUS_RTT_RETRANSMISSIONS:
                    self.rto_estimator.clear_rtt()
                self.retransmissions += 1
                # Hacer back-off del RTO y luego retransmitir el paquete más
                # viejo que aún no fue reconocido.
                self.rto_estimator.back_off_rto()
                packet = self.rqueue.head()
                self.send_and_queue(packet, is_retransmission=True)

            if self.write_stream_open or \
               self.control_block.has_data_to_send():
                self.attempt_to_send_data()
            else:
                # Mandar FIN cuando:
                #   * El stream de escritura está cerrado,
                #   * El estado es ESTABLISHED/CLOSE_WAIT
                #     (i.e., todavía no se ha enviado un FIN), y
                #   * Todo byte saliente fue exitosamente reconocido.
                self.attempt_to_send_FIN()

    def attempt_to_send_data(self):
        window_closed = False
        while self.control_block.has_data_to_send() and not window_closed:
            seq_number = self.control_block.get_snd_nxt()
            to_send = self.control_block.extract_from_out_buffer(MSS)
            if not to_send:
                # El bloque de control no devolvió nada, lo cual es un indicio
                # de que la ventana está cerrada. Luego, no tenemos nada más
                # por hacer hasta que lleguen los próximos ACKs.
                window_closed = True
            else:
                packet = self.build_packet(payload=to_send, seq=seq_number)
                self.send_and_queue(packet)

    def attempt_to_send_FIN(self):
        state_allows_closing = self.state in [ESTABLISHED, CLOSE_WAIT]
        if state_allows_closing and self.rqueue.empty():
            fin_packet = self.build_packet(flags=[ACKFlag, FINFlag])
            # Estamos enviando un FIN, y este flag se secuencia. Incrementar el
            # siguiente byte a enviar.
            self.control_block.increment_snd_nxt()
            new_state = FIN_WAIT1 if self.state == ESTABLISHED else LAST_ACK
            self.set_state(new_state)
            self.send_and_queue(fin_packet)

    def handle_incoming(self, packet):
        self.packet_handler.handle(packet)
        self.packet_sender.notify()

    def shutdown(self, how):
        if how == SHUT_RD:
            self.shutdown_read_stream()
        elif how == SHUT_WR:
            self.shutdown_write_stream()
        else:
            self.shutdown_read_stream()
            self.shutdown_write_stream()

    def shutdown_read_stream(self):
        self.read_stream_open = False

    def shutdown_write_stream(self):
        self.write_stream_open = False
        self.packet_sender.notify()

    def close(self, mode=NO_WAIT):
        self.close_mode = mode
        if self.state != CLOSED:
            self.shutdown(SHUT_RDWR)
            self.close_event.wait()
        self.free()
        self.join_threads()

    def free(self):
        if self.control_block is not None:
            self.control_block.flush_buffers()
        self.stop_threads()
        # En caso de que el establecimiento de conexión haya fallado, esto
        # destrabará al thread principal de la aplicación.
        self.connected_event.set()
        # Y, análogamente, esto destrabará al thread principal si se llama a
        # close y free es luego invocada por algún otro thread.
        self.close_event.set()
        self.set_state(CLOSED)
示例#2
0
文件: protocol.py 项目: lukius/ptc
class PTCProtocol(object):
    
    def __init__(self):
        self.state = CLOSED
        self.control_block = None
        self.packet_builder = PacketBuilder()
        self.socket = Soquete()
        self.rcv_wnd = RECEIVE_BUFFER_SIZE
        self.iss = self.compute_iss()
        self.rqueue = RetransmissionQueue()
        self.read_stream_open = True
        self.write_stream_open = True
        self.packet_handler = IncomingPacketHandler(self)
        self.rto_estimator = RTOEstimator(self)
        self.ticks = 0
        self.retransmissions = 0
        self.close_mode = NO_WAIT
        self.close_event = threading.Event()
        self.initialize_threads()
        self.initialize_timers()
        
    def initialize_threads(self):
        self.packet_sender = PacketSender(self)
        self.packet_receiver = PacketReceiver(self)
        self.clock = Clock(self)
    
    def initialize_timers(self):
        self.retransmission_timer = RetransmissionTimer(self)
        
    def start_threads(self):
        self.packet_receiver.start()
        self.packet_sender.start()
        self.clock.start()
        
    def stop_threads(self):
        self.packet_receiver.stop()
        self.packet_sender.stop()
        self.packet_sender.notify()
        self.clock.stop()
        
    def join_threads(self):
        self.packet_receiver.join()
        self.packet_sender.join()
        self.clock.join()
        
    def set_state(self, state):
        self.state = state
        if state == CLOSED or\
           (self.close_mode == NO_WAIT and state == FIN_WAIT2):
            # Signal this event when the connection is completely closed or
            # otherwise if the user explicitly chose to wait for the other
            # party to close also. By default, this behavior resembles TCP
            # (i.e., asymmetric close).
            self.close_event.set()
        if state == ESTABLISHED:
            self.connected_event.set()
    
    def compute_iss(self):
        value = random.randint(0, MAX_SEQ)
        return SequenceNumber(value)
        
    def initialize_control_block_from(self, packet):
        # +1 since the SYN flag is also sequenced. 
        receive_seq = 1 + packet.get_seq_number()
        send_seq = 1 + self.iss
        send_window = packet.get_window_size()
        receive_window = self.rcv_wnd
        self.control_block = PTCControlBlock(send_seq, receive_seq,
                                             send_window, receive_window)
    
    def is_connected(self):
        connected_states = [ESTABLISHED, FIN_WAIT1, FIN_WAIT2, CLOSE_WAIT,
                            CLOSING, LAST_ACK]
        return self.state in connected_states
        
    def build_packet(self, seq=None, ack=None, payload=None, flags=None,
                     window=None):
        if seq is None:
            seq = self.control_block.get_snd_nxt()
        if flags is None:
            flags = [ACKFlag]
        if ack is None and ACKFlag in flags:
            ack = self.control_block.get_rcv_nxt()
        if window is None:
            window = self.control_block.get_rcv_wnd()
        packet = self.packet_builder.build(payload=payload, flags=flags,
                                           seq=seq, ack=ack, window=window)
        return packet

    def send_and_queue(self, packet, is_retransmission=False):
        if is_retransmission:
            # Karn's algorithm: do not use retransmitted packets to update
            # RTO estimations.
            if self.rto_estimator.is_tracking_packets():
                tracked_packet = self.rto_estimator.get_tracked_packet()
                tracked_seq = tracked_packet.get_seq_number()
                if tracked_seq == packet.get_seq_number():
                    self.rto_estimator.untrack()
        else:
            # Only fresh packets will be tracked for their RTTs (Karn's
            # algorithm once more).
            if not self.rto_estimator.is_tracking_packets():
                self.rto_estimator.track(packet)
            # Enqueue this fresh packet for eventual retransmissions.
            # Retransmissions are not re-enqueued since they remain at the
            # head of the queue until they are acknowledged.
            self.rqueue.put(packet)
            
        if not self.retransmission_timer.is_running():
            # Use current RTO estimation to time this packet.
            current_rto = self.rto_estimator.get_current_rto()
            self.retransmission_timer.start(current_rto)
            
        self.socket.send(packet)
        
    def set_destination_on_packet_builder(self, address, port):
        self.packet_builder.set_destination_address(address)
        self.packet_builder.set_destination_port(port)        
        
    def bind(self, address, port):
        self.socket.bind(address, port)
        self.packet_builder.set_source_address(address)
        self.packet_builder.set_source_port(port)
    
    def listen(self):
        self.set_state(LISTEN)
        
    def connect_to(self, address, port):
        self.connected_event = threading.Event()
        self.set_destination_on_packet_builder(address, port)
        self.start_threads()
        
        syn_packet = self.build_packet(seq=self.iss, flags=[SYNFlag],
                                       window=self.rcv_wnd)
        self.set_state(SYN_SENT)
        self.send_and_queue(syn_packet)
        
        self.connected_event.wait()

    def accept(self):
        if self.state != LISTEN:
            raise PTCError('should listen first')
        self.connected_event = threading.Event()
        self.start_threads()
        # Wait until client attempts to connect.
        self.connected_event.wait()        
        
    def send(self, data):
        with self.control_block:
            if not self.write_stream_open:
                raise PTCError('write stream is closed')
            self.control_block.to_out_buffer(data)
            self.packet_sender.notify()
        
    def receive(self, size):
        data = self.control_block.from_in_buffer(size)
        updated_rcv_wnd = self.control_block.get_rcv_wnd()
        if updated_rcv_wnd > 0:
            wnd_packet = self.build_packet(window=updated_rcv_wnd)
            self.socket.send(wnd_packet)
        return data

    def get_ticks(self):
        return self.ticks

    def tick(self):
        self.ticks += 1
        self.retransmission_timer.tick()

    def acknowledge_packets_and_update_timers_with(self, packet):
        ack_number = packet.get_ack_number()
        if self.control_block.ack_is_accepted(ack_number):
            # First, pass this packet to the RTO estimator in order to update
            # its values if appropriate (that is, if the packet is acknowledging
            # the packet tracked by it).
            self.rto_estimator.process_ack(packet)
            # Then, remove enqueued packets and stop/restart the retransmission
            # timer as required.
            self.remove_from_retransmission_queue_packets_acked_by(packet)

    def remove_from_retransmission_queue_packets_acked_by(self, packet):
        # Only ACK numbers greater than SND_UNA and less than SND_NXT are
        # valid here.
        with self.rqueue:
            snd_una = self.control_block.get_snd_una()
            snd_nxt = self.control_block.get_snd_nxt()
            # See which packets already enqueued are acknowledged by this
            # packet. SND_UNA and SND_NXT are needed for properly comparing
            # SEQs and ACKs.
            removed_packets = self.rqueue.remove_acknowledged_by(packet,
                                                                 snd_una,
                                                                 snd_nxt)

            if len(removed_packets) > 0:
                # Some packets were acked, and so we must update the
                # retransmission timer accordingly.
                self.adjust_retransmission_timer()
                
    def adjust_retransmission_timer(self):
        # Start at zero retransmissions for next packet.
        self.retransmissions = 0
        if self.rqueue.empty():
            # All outstanding data was acknowledged. Stop timer.
            self.retransmission_timer.stop()
        else:
            # Some data was acknowledged, but some still remains.
            # Restart the timer using current RTO estimation.
            current_rto = self.rto_estimator.get_current_rto()
            self.retransmission_timer.restart(current_rto)

    def handle_outgoing(self):
        if self.control_block is None:
            # When connection is still not established, we don't have
            # anything to send.
            return
        with self.control_block:
            # See first if we have a transmission timeout.
            if self.retransmission_timer.has_expired() and\
               not self.rqueue.empty():
                # If we reached the maximum retransmissions allowed,
                # release the connection.
                if self.retransmissions >= MAX_RETRANSMISSION_ATTEMPTS:
                    self.free()
                    return
                # Clear RTT estimation; it was backed off several times and so
                # it might no longer represent the actual RTT. 
                if self.retransmissions > BOGUS_RTT_RETRANSMISSIONS:
                    self.rto_estimator.clear_rtt()
                self.retransmissions += 1
                # Back off RTO and then retransmit the earliest packet not yet
                # acknowledged.
                self.rto_estimator.back_off_rto()
                packet = self.rqueue.head()
                self.send_and_queue(packet, is_retransmission=True)
            
            if self.write_stream_open or \
               self.control_block.has_data_to_send():
                self.attempt_to_send_data()
            else:
                # Send FIN when:
                #   * Write stream is closed,
                #   * State is ESTABLISHED/CLOSE_WAIT
                #     (i.e., FIN was not yet sent), and
                #   * Every outgoing byte was successfully acknowledged.
                self.attempt_to_send_FIN()

    def attempt_to_send_data(self):
        window_closed = False
        while self.control_block.has_data_to_send() and not window_closed:
            seq_number = self.control_block.get_snd_nxt()
            to_send = self.control_block.extract_from_out_buffer(MSS)
            if not to_send:
                # Control block returned nothing, which hints that the window
                # is closed. Thus, we have nothing else to do until further
                # ACKs arrive.
                window_closed = True
            else:
                packet = self.build_packet(payload=to_send, seq=seq_number)
                self.send_and_queue(packet)
                
    def attempt_to_send_FIN(self):
        state_allows_closing = self.state in [ESTABLISHED, CLOSE_WAIT]
        if state_allows_closing and self.rqueue.empty():
            fin_packet = self.build_packet(flags=[ACKFlag, FINFlag])
            # We are sending a FIN packet, and this flag is sequenced. Move
            # forward the next byte sequence to be sent.
            self.control_block.increment_snd_nxt()
            new_state = FIN_WAIT1 if self.state == ESTABLISHED else LAST_ACK
            self.set_state(new_state)
            self.send_and_queue(fin_packet)
    
    def handle_incoming(self, packet):
        self.packet_handler.handle(packet)
        self.packet_sender.notify()
    
    def shutdown(self, how):
        if how == SHUT_RD:
            self.shutdown_read_stream()
        elif how == SHUT_WR:
            self.shutdown_write_stream()
        else:
            self.shutdown_read_stream()
            self.shutdown_write_stream()
            
    def shutdown_read_stream(self):
        self.read_stream_open = False
    
    def shutdown_write_stream(self):
        self.write_stream_open = False
        self.packet_sender.notify()
        
    def close(self, mode=NO_WAIT):
        self.close_mode = mode
        if self.state != CLOSED:
            self.shutdown(SHUT_RDWR)
            self.close_event.wait()
        self.free()
        self.join_threads()
            
    def free(self):
        if self.control_block is not None:
            self.control_block.flush_buffers()
        self.stop_threads()
        # In case connection establishment failed, this will unlock the main
        # thread.
        self.connected_event.set()
        # And, similarly, this will unlock the main thread if close is called
        # and free is later invoked by some other thread, for whatever reason.
        self.close_event.set()
        self.set_state(CLOSED)
示例#3
0
class PTCProtocol(object):
    def __init__(self, ack_delay=0, ack_loss_probability=0):
        # Modificación
        self.ack_delay = ack_delay
        self.ack_loss_probability = ack_loss_probability
        self.retransmissions = 0
        logging.basicConfig(
            level=logging.DEBUG,
            format='%(asctime)s:%(levelname)s:%(name)s:%(message)s',
            filename='ptc.log')
        self.logger = logging.getLogger('PTCProtocol')

        self.state = CLOSED
        self.control_block = None
        self.packet_builder = PacketBuilder()
        self.socket = Soquete(protocol=self)
        self.rcv_wnd = RECEIVE_BUFFER_SIZE
        self.iss = self.compute_iss()
        self.rqueue = RetransmissionQueue()
        self.retransmission_attempts = dict()
        self.read_stream_open = True
        self.write_stream_open = True
        self.packet_handler = IncomingPacketHandler(self)
        self.close_event = threading.Event()
        self.initialize_threads()

    def initialize_threads(self):
        self.packet_sender = PacketSender(self)
        self.packet_receiver = PacketReceiver(self)
        self.clock = Clock(self)
        self.keepalive_sender = KeepAliveSender(self)

    def start_threads(self):
        self.packet_receiver.start()
        self.packet_sender.start()
        self.clock.start()
        self.keepalive_sender.start()

    def stop_threads(self):
        self.packet_receiver.stop()
        self.packet_sender.stop()
        self.packet_sender.notify()
        self.clock.stop()
        self.keepalive_sender.stop()

    def join_threads(self):
        self.packet_receiver.join()
        self.packet_sender.join()
        self.clock.join()
        self.keepalive_sender.join()

    def set_state(self, state):
        self.state = state
        if state == CLOSED or state == FIN_WAIT2:
            self.close_event.set()
        if state == ESTABLISHED:
            self.connected_event.set()

    def compute_iss(self):
        value = random.randint(0, MAX_SEQ)
        return SequenceNumber(value)

    def initialize_control_block_from(self, packet):
        # +1 dado que el SYN se secuencia.
        receive_seq = 1 + packet.get_seq_number()
        send_seq = 1 + self.iss
        send_window = packet.get_window_size()
        receive_window = self.rcv_wnd
        self.control_block = PTCControlBlock(send_seq, receive_seq,
                                             send_window, receive_window)

    def is_connected(self):
        connected_states = [
            ESTABLISHED, FIN_WAIT1, FIN_WAIT2, CLOSE_WAIT, CLOSING, LAST_ACK
        ]
        return self.state in connected_states

    def build_packet(self,
                     seq=None,
                     ack=None,
                     payload=None,
                     flags=None,
                     window=None):
        if seq is None:
            seq = self.control_block.get_snd_nxt()
        if flags is None:
            flags = [ACKFlag]
        if ack is None and ACKFlag in flags:
            ack = self.control_block.get_rcv_nxt()
        if window is None:
            window = self.control_block.get_rcv_wnd()
        packet = self.packet_builder.build(payload=payload,
                                           flags=flags,
                                           seq=seq,
                                           ack=ack,
                                           window=window)
        return packet

    def send_and_queue(self, packet):
        self.rqueue.put(packet)
        self.socket.send(packet)

    def set_destination_on_packet_builder(self, address, port):
        self.packet_builder.set_destination_address(address)
        self.packet_builder.set_destination_port(port)

    def bind(self, address, port):
        self.socket.bind(address, port)
        self.packet_builder.set_source_address(address)
        self.packet_builder.set_source_port(port)

    def listen(self):
        self.set_state(LISTEN)

    def connect_to(self, address, port):
        self.connected_event = threading.Event()
        self.set_destination_on_packet_builder(address, port)
        self.start_threads()

        syn_packet = self.build_packet(seq=self.iss,
                                       flags=[SYNFlag],
                                       window=self.rcv_wnd)
        self.set_state(SYN_SENT)
        self.send_and_queue(syn_packet)

        self.connected_event.wait()

    def accept(self):
        if self.state != LISTEN:
            raise PTCError('should listen first')
        self.connected_event = threading.Event()
        self.start_threads()
        # Esperar hasta que un cliente desee conectarse.
        self.connected_event.wait()

    def send(self, data):
        with self.control_block:
            if not self.write_stream_open:
                raise PTCError('write stream is closed')
            self.control_block.to_out_buffer(data)
            self.packet_sender.notify()

    def receive(self, size):
        data = self.control_block.from_in_buffer(size)
        updated_rcv_wnd = self.control_block.get_rcv_wnd()
        if updated_rcv_wnd > 0:
            wnd_packet = self.build_packet(window=updated_rcv_wnd)
            self.logger.debug('Enviando window: window=%d' % updated_rcv_wnd)
            self.socket.send(wnd_packet)
        return data

    def tick(self):

        with self.rqueue:
            self.rqueue.tick()
            self.retransmit_packets_if_needed()

    def send_keepalive(self):
        rcv_wnd = self.control_block.get_rcv_wnd()
        packet = self.build_packet(window=rcv_wnd)
        self.logger.debug('Enviando keepalive: window=%d' % rcv_wnd)
        self.socket.send(packet)

    def retransmit_packets_if_needed(self):
        to_retransmit = self.rqueue.get_packets_to_retransmit()
        for packet in to_retransmit:
            attempts = self.update_retransmission_attempts_for(packet)
            self.logger.debug('Retransmitiendo: intento %d' % attempts)
            if attempts > MAX_RETRANSMISSION_ATTEMPTS:
                self.logger.debug('Límite de retransmisiones alcanzado')
                # Nos damos por vencidos. Se superó el máximo número de
                # retransmisiones para este paquete.
                self.free()
            else:
                self.retransmissions += 1
                self.send_and_queue(packet)

    def update_retransmission_attempts_for(self, packet):
        seq_number = packet.get_seq_number()
        attempts = 1 + self.retransmission_attempts.setdefault(seq_number, 0)
        self.retransmission_attempts[seq_number] = attempts
        return attempts

    def acknowledge_packets_on_retransmission_queue_with(self, packet):
        ack_number = packet.get_ack_number()
        if self.control_block.ack_is_accepted(ack_number):
            # Sólo ACKs mayores que SND_UNA y menores que SND_NXT son válidos
            # en este punto.
            with self.rqueue:
                snd_una = self.control_block.get_snd_una()
                snd_nxt = self.control_block.get_snd_nxt()
                # Ver qué paquetes encolados son totalmente reconocidos por
                # este paquete. Necesitamos SND_UNA y SND_NXT para poder
                # comparar correctamente los SEQs y ACKs.
                removed_packets = self.rqueue.remove_acknowledged_by(
                    packet, snd_una, snd_nxt)
                for removed_packet in removed_packets:
                    seq_number = removed_packet.get_seq_number()
                    if seq_number in self.retransmission_attempts:
                        del self.retransmission_attempts[seq_number]

    def handle_outgoing(self):
        if self.control_block is None:
            # Cuando la conexión todavía no fue establecida, no tenemos nada
            # para enviar.
            return
        with self.control_block:
            if self.write_stream_open or self.control_block.has_data_to_send():
                self.attempt_to_send_data()
            else:
                # Mandar FIN cuando:
                #   * El stream de escritura está cerrado,
                #   * El estado es ESTABLISHED/CLOSE_WAIT
                #     (i.e., todavía no se ha enviado un FIN), y
                #   * Todo byte saliente fue exitosamente reconocido.
                self.attempt_to_send_FIN()

    def attempt_to_send_data(self):
        window_closed = False
        while self.control_block.has_data_to_send() and not window_closed:
            seq_number = self.control_block.get_snd_nxt()
            to_send = self.control_block.extract_from_out_buffer(MSS)
            if not to_send:
                # El bloque de control no devolvió nada, lo cual es un indicio
                # de que la ventana está cerrada. Luego, no tenemos nada más
                # por hacer hasta que lleguen los próximos ACKs.
                window_closed = True
            else:
                packet = self.build_packet(payload=to_send, seq=seq_number)
                self.send_and_queue(packet)

    def attempt_to_send_FIN(self):
        state_allows_closing = self.state in [ESTABLISHED, CLOSE_WAIT]
        if state_allows_closing and self.rqueue.empty():
            fin_packet = self.build_packet(flags=[ACKFlag, FINFlag])
            # Estamos enviando un FIN, y este flag se secuencia. Incrementar el
            # siguiente byte a enviar.
            self.control_block.increment_snd_nxt()
            new_state = FIN_WAIT1 if self.state == ESTABLISHED else LAST_ACK
            self.set_state(new_state)
            self.send_and_queue(fin_packet)

    def handle_incoming(self, packet):
        self.packet_handler.handle(packet)
        self.packet_sender.notify()

    def shutdown(self, how):
        if how == SHUT_RD:
            self.shutdown_read_stream()
        elif how == SHUT_WR:
            self.shutdown_write_stream()
        else:
            self.shutdown_read_stream()
            self.shutdown_write_stream()

    def shutdown_read_stream(self):
        self.read_stream_open = False

    def shutdown_write_stream(self):
        self.write_stream_open = False
        self.packet_sender.notify()

    def close(self):
        if self.state != CLOSED:
            self.shutdown(SHUT_RDWR)
            self.close_event.wait()
        self.free()
        self.join_threads()

    def free(self):
        if self.control_block is not None:
            self.control_block.flush_buffers()
        self.stop_threads()
        # En caso de que el establecimiento de conexión haya fallado, esto
        # destrabará al thread principal de la aplicación.
        self.connected_event.set()
        # Y, análogamente, esto destrabará al thread principal si se llama a
        # close y free es luego invocada por algún otro thread.
        self.close_event.set()
        self.set_state(CLOSED)
示例#4
0
class PTCProtocol(object):
    
    def __init__(self, ack_delay=0, ack_loss_probability=0):
        # Modificación
        self.ack_delay = ack_delay
        self.ack_loss_probability = ack_loss_probability
        self.retransmissions = 0
        logging.basicConfig(level=logging.DEBUG,
                            format='%(asctime)s:%(levelname)s:%(name)s:%(message)s',
                            filename='ptc.log')
        self.logger = logging.getLogger('PTCProtocol')

        self.state = CLOSED
        self.control_block = None
        self.packet_builder = PacketBuilder()
        self.socket = Soquete(protocol=self)
        self.rcv_wnd = RECEIVE_BUFFER_SIZE        
        self.iss = self.compute_iss()
        self.rqueue = RetransmissionQueue()
        self.retransmission_attempts = dict()
        self.read_stream_open = True
        self.write_stream_open = True
        self.packet_handler = IncomingPacketHandler(self) 
        self.close_event = threading.Event()
        self.initialize_threads()
        
    def initialize_threads(self):
        self.packet_sender = PacketSender(self)
        self.packet_receiver = PacketReceiver(self)
        self.clock = Clock(self)
        self.keepalive_sender = KeepAliveSender(self)
        
    def start_threads(self):
        self.packet_receiver.start()
        self.packet_sender.start()
        self.clock.start()
        self.keepalive_sender.start()
        
    def stop_threads(self):
        self.packet_receiver.stop()
        self.packet_sender.stop()
        self.packet_sender.notify()
        self.clock.stop()
        self.keepalive_sender.stop()
        
    def join_threads(self):
        self.packet_receiver.join()
        self.packet_sender.join()
        self.clock.join()
        self.keepalive_sender.join()
        
    def set_state(self, state):
        self.state = state
        if state == CLOSED or state == FIN_WAIT2:
            self.close_event.set()
        if state == ESTABLISHED:
            self.connected_event.set()
    
    def compute_iss(self):
        value = random.randint(0, MAX_SEQ)
        return SequenceNumber(value)
        
    def initialize_control_block_from(self, packet):
        # +1 dado que el SYN se secuencia. 
        receive_seq = 1 + packet.get_seq_number()
        send_seq = 1 + self.iss
        send_window = packet.get_window_size()
        receive_window = self.rcv_wnd
        self.control_block = PTCControlBlock(send_seq, receive_seq,
                                             send_window, receive_window)
    
    def is_connected(self):
        connected_states = [ESTABLISHED, FIN_WAIT1, FIN_WAIT2, CLOSE_WAIT,
                            CLOSING, LAST_ACK]
        return self.state in connected_states
        
    def build_packet(self, seq=None, ack=None, payload=None, flags=None,
                     window=None):
        if seq is None:
            seq = self.control_block.get_snd_nxt()
        if flags is None:
            flags = [ACKFlag]
        if ack is None and ACKFlag in flags:
            ack = self.control_block.get_rcv_nxt()
        if window is None:
            window = self.control_block.get_rcv_wnd()
        packet = self.packet_builder.build(payload=payload, flags=flags,
                                           seq=seq, ack=ack, window=window)
        return packet
        
    def send_and_queue(self, packet):
        self.rqueue.put(packet)
        self.socket.send(packet)
        
    def set_destination_on_packet_builder(self, address, port):
        self.packet_builder.set_destination_address(address)
        self.packet_builder.set_destination_port(port)        
        
    def bind(self, address, port):
        self.socket.bind(address, port)
        self.packet_builder.set_source_address(address)
        self.packet_builder.set_source_port(port)
    
    def listen(self):
        self.set_state(LISTEN)
        
    def connect_to(self, address, port):
        self.connected_event = threading.Event()
        self.set_destination_on_packet_builder(address, port)
        self.start_threads()
        
        syn_packet = self.build_packet(seq=self.iss, flags=[SYNFlag],
                                       window=self.rcv_wnd)
        self.set_state(SYN_SENT)
        self.send_and_queue(syn_packet)
        
        self.connected_event.wait()

    def accept(self):
        if self.state != LISTEN:
            raise PTCError('should listen first')
        self.connected_event = threading.Event()
        self.start_threads()
        # Esperar hasta que un cliente desee conectarse.
        self.connected_event.wait()        
        
    def send(self, data):
        with self.control_block:
            if not self.write_stream_open:
                raise PTCError('write stream is closed')
            self.control_block.to_out_buffer(data)
            self.packet_sender.notify()
        
    def receive(self, size):
        data = self.control_block.from_in_buffer(size)
        updated_rcv_wnd = self.control_block.get_rcv_wnd()
        if updated_rcv_wnd > 0:
            wnd_packet = self.build_packet(window=updated_rcv_wnd)
            self.logger.debug('Enviando window: window=%d' % updated_rcv_wnd)
            self.socket.send(wnd_packet)
        return data
    
    def tick(self):

        with self.rqueue:
            self.rqueue.tick()
            self.retransmit_packets_if_needed()
    
    def send_keepalive(self):
        rcv_wnd = self.control_block.get_rcv_wnd()
        packet = self.build_packet(window=rcv_wnd)
        self.logger.debug('Enviando keepalive: window=%d' % rcv_wnd)
        self.socket.send(packet)

    def retransmit_packets_if_needed(self):
        to_retransmit = self.rqueue.get_packets_to_retransmit()
        for packet in to_retransmit:
            attempts = self.update_retransmission_attempts_for(packet)
            self.logger.debug('Retransmitiendo: intento %d' % attempts)
            if attempts > MAX_RETRANSMISSION_ATTEMPTS:
                self.logger.debug('Límite de retransmisiones alcanzado')
                # Nos damos por vencidos. Se superó el máximo número de
                # retransmisiones para este paquete.
                self.free()
            else:
                self.retransmissions += 1
                self.send_and_queue(packet)
    
    def update_retransmission_attempts_for(self, packet):
        seq_number = packet.get_seq_number()
        attempts = 1 + self.retransmission_attempts.setdefault(seq_number, 0)
        self.retransmission_attempts[seq_number] = attempts
        return attempts
    
    def acknowledge_packets_on_retransmission_queue_with(self, packet):
        ack_number = packet.get_ack_number()
        if self.control_block.ack_is_accepted(ack_number):
            # Sólo ACKs mayores que SND_UNA y menores que SND_NXT son válidos
            # en este punto.
            with self.rqueue:
                snd_una = self.control_block.get_snd_una()
                snd_nxt = self.control_block.get_snd_nxt()
                # Ver qué paquetes encolados son totalmente reconocidos por
                # este paquete. Necesitamos SND_UNA y SND_NXT para poder
                # comparar correctamente los SEQs y ACKs.
                removed_packets = self.rqueue.remove_acknowledged_by(packet,
                                                                     snd_una,
                                                                     snd_nxt)
                for removed_packet in removed_packets:
                    seq_number = removed_packet.get_seq_number()
                    if seq_number in self.retransmission_attempts:
                        del self.retransmission_attempts[seq_number]
        
    def handle_outgoing(self):
        if self.control_block is None:
            # Cuando la conexión todavía no fue establecida, no tenemos nada
            # para enviar. 
            return
        with self.control_block:
            if self.write_stream_open or self.control_block.has_data_to_send():
                self.attempt_to_send_data()
            else:
                # Mandar FIN cuando:
                #   * El stream de escritura está cerrado,
                #   * El estado es ESTABLISHED/CLOSE_WAIT
                #     (i.e., todavía no se ha enviado un FIN), y
                #   * Todo byte saliente fue exitosamente reconocido.
                self.attempt_to_send_FIN()
            
    def attempt_to_send_data(self):
        window_closed = False
        while self.control_block.has_data_to_send() and not window_closed:
            seq_number = self.control_block.get_snd_nxt()
            to_send = self.control_block.extract_from_out_buffer(MSS)
            if not to_send:
                # El bloque de control no devolvió nada, lo cual es un indicio
                # de que la ventana está cerrada. Luego, no tenemos nada más
                # por hacer hasta que lleguen los próximos ACKs.
                window_closed = True
            else:
                packet = self.build_packet(payload=to_send, seq=seq_number)
                self.send_and_queue(packet)
                
    def attempt_to_send_FIN(self):
        state_allows_closing = self.state in [ESTABLISHED, CLOSE_WAIT]
        if state_allows_closing and self.rqueue.empty():
            fin_packet = self.build_packet(flags=[ACKFlag, FINFlag])
            # Estamos enviando un FIN, y este flag se secuencia. Incrementar el
            # siguiente byte a enviar.
            self.control_block.increment_snd_nxt()
            new_state = FIN_WAIT1 if self.state == ESTABLISHED else LAST_ACK
            self.set_state(new_state)
            self.send_and_queue(fin_packet)
            
    def handle_incoming(self, packet):
        self.packet_handler.handle(packet)
        self.packet_sender.notify()
        
    def shutdown(self, how):
        if how == SHUT_RD:
            self.shutdown_read_stream()
        elif how == SHUT_WR:
            self.shutdown_write_stream()
        else:
            self.shutdown_read_stream()
            self.shutdown_write_stream()
            
    def shutdown_read_stream(self):
        self.read_stream_open = False
    
    def shutdown_write_stream(self):
        self.write_stream_open = False
        self.packet_sender.notify()
        
    def close(self):
        if self.state != CLOSED:
            self.shutdown(SHUT_RDWR)
            self.close_event.wait()
        self.free()
        self.join_threads()
            
    def free(self):
        if self.control_block is not None:
            self.control_block.flush_buffers()
        self.stop_threads()
        # En caso de que el establecimiento de conexión haya fallado, esto
        # destrabará al thread principal de la aplicación.
        self.connected_event.set()
        # Y, análogamente, esto destrabará al thread principal si se llama a
        # close y free es luego invocada por algún otro thread.
        self.close_event.set()
        self.set_state(CLOSED)
示例#5
0
class PTCProtocol(object):
    def __init__(self,
                 ack_delay=0,
                 ack_loss_probability=0,
                 packet_loss_probability=0,
                 alpha=0.8,
                 beta=0.4,
                 k=4,
                 filepath='rto.dat'):
        self.ack_delay = ack_delay
        self.ack_loss_probability = ack_loss_probability
        print 'self.ack_loss_probability: ' + str(self.ack_loss_probability)
        self.packet_loss_probability = packet_loss_probability
        self.beta = beta
        self.alpha = alpha
        self.k = k
        self.state = CLOSED
        self.control_block = None
        self.packet_builder = PacketBuilder()
        self.socket = Soquete()
        self.rcv_wnd = RECEIVE_BUFFER_SIZE
        self.iss = self.compute_iss()
        self.rqueue = RetransmissionQueue()
        self.read_stream_open = True
        self.write_stream_open = True
        self.packet_handler = IncomingPacketHandler(self)
        self.rto_estimator = RTOEstimator(self)
        self.ticks = 0
        self.retransmissions = 0
        self.close_mode = NO_WAIT
        self.close_event = threading.Event()
        self.initialize_threads()
        self.initialize_timers()
        self.filepath = filepath
        self.retransmission_counter = 0

    def initialize_threads(self):
        self.packet_sender = PacketSender(self)
        self.packet_receiver = PacketReceiver(self)
        self.clock = Clock(self)

    def initialize_timers(self):
        self.retransmission_timer = RetransmissionTimer(self)

    def start_threads(self):
        self.packet_receiver.start()
        self.packet_sender.start()
        self.clock.start()

    def stop_threads(self):
        self.packet_receiver.stop()
        self.packet_sender.stop()
        self.packet_sender.notify()
        self.clock.stop()

    def join_threads(self):
        self.packet_receiver.join()
        self.packet_sender.join()
        self.clock.join()

    def set_state(self, state):
        self.state = state
        if state == CLOSED or\
           (self.close_mode == NO_WAIT and state == FIN_WAIT2):
            # Señalizar este evento cuando la conexión queda completamente
            # cerrada o bien cuando el usuario del socket explícitamente
            # eligió esperar a que el interlocutor también cierre. Por
            # defecto, el comportamiento es similar al de TCP (i.e., cierre
            # asimétrico).
            self.close_event.set()
        if state == ESTABLISHED:
            self.connected_event.set()

    def compute_iss(self):
        value = random.randint(0, MAX_SEQ / 2)
        return SequenceNumber(value)

    def initialize_control_block_from(self, packet):
        # +1 dado que el SYN se secuencia.
        receive_seq = 1 + packet.get_seq_number()
        send_seq = 1 + self.iss
        send_window = packet.get_window_size()
        receive_window = self.rcv_wnd
        self.control_block = PTCControlBlock(send_seq, receive_seq,
                                             send_window, receive_window)

    def is_connected(self):
        connected_states = [
            ESTABLISHED, FIN_WAIT1, FIN_WAIT2, CLOSE_WAIT, CLOSING, LAST_ACK
        ]
        return self.state in connected_states

    def build_packet(self,
                     seq=None,
                     ack=None,
                     payload=None,
                     flags=None,
                     window=None):
        if seq is None:
            seq = self.control_block.get_snd_nxt()
        if flags is None:
            flags = [ACKFlag]
        if ack is None and ACKFlag in flags:
            ack = self.control_block.get_rcv_nxt()
        if window is None:
            window = self.control_block.get_rcv_wnd()
        packet = self.packet_builder.build(payload=payload,
                                           flags=flags,
                                           seq=seq,
                                           ack=ack,
                                           window=window)
        return packet

    def send_and_queue(self, packet, is_retransmission=False):
        if is_retransmission:
            self.retransmission_counter = self.retransmission_counter + 1
            # Algoritmo de Karn: no usar paquetes retransmitidos para
            # actualizar las estimaciones del RTO.
            if self.rto_estimator.is_tracking_packets():
                tracked_packet = self.rto_estimator.get_tracked_packet()
                tracked_seq = tracked_packet.get_seq_number()
                if tracked_seq == packet.get_seq_number():
                    self.rto_estimator.untrack()
        else:
            # Sólo se hará seguimiento de paquetes frescos para estimar el
            # RTT (otra vez por el algoritmo de Karn).
            if not self.rto_estimator.is_tracking_packets():
                self.rto_estimator.track(packet)
            # Encolar este paquete fresco para eventuales retransmisiones.
            # Las retransmisiones no se reencolan pues quedan al principio de
            # la cola hasta que son correctamente reconocidas.
            self.rqueue.put(packet)

        if not self.retransmission_timer.is_running():
            # Usar la estimación actual del RTO para medir este paquete.
            current_rto = self.rto_estimator.get_current_rto()
            self.retransmission_timer.start(current_rto)

        if self.state == ESTABLISHED and FINFlag not in packet:
            if random.uniform(0, 1) < self.packet_loss_probability:
                return
        self.socket.send(packet)

    def set_destination_on_packet_builder(self, address, port):
        self.packet_builder.set_destination_address(address)
        self.packet_builder.set_destination_port(port)

    def bind(self, address, port):
        self.socket.bind(address, port)
        self.packet_builder.set_source_address(address)
        self.packet_builder.set_source_port(port)

    def listen(self):
        self.set_state(LISTEN)

    def connect_to(self, address, port):
        self.connected_event = threading.Event()
        self.set_destination_on_packet_builder(address, port)
        self.start_threads()

        syn_packet = self.build_packet(seq=self.iss,
                                       flags=[SYNFlag],
                                       window=self.rcv_wnd)
        self.set_state(SYN_SENT)
        self.send_and_queue(syn_packet)

        self.connected_event.wait()

    def accept(self):
        if self.state != LISTEN:
            raise PTCError('should listen first')
        self.connected_event = threading.Event()
        self.start_threads()
        # Esperar hasta que un cliente desee conectarse.
        self.connected_event.wait()

    def send(self, data):
        with self.control_block:
            if not self.write_stream_open:
                raise PTCError('write stream is closed')
            self.control_block.to_out_buffer(data)
            self.packet_sender.notify()

    def receive(self, size):
        data = self.control_block.from_in_buffer(size)
        updated_rcv_wnd = self.control_block.get_rcv_wnd()
        if updated_rcv_wnd > 0:
            wnd_packet = self.build_packet(window=updated_rcv_wnd)
            self.socket.send(wnd_packet)
        return data

    def get_ticks(self):
        return self.ticks

    def tick(self):
        self.ticks += 1
        self.retransmission_timer.tick()

    def acknowledge_packets_and_update_timers_with(self, packet):
        ack_number = packet.get_ack_number()
        if self.control_block.ack_is_accepted(ack_number):
            # Primero, pasar este paquete al estimador de RTO para que pueda
            # actualizar sus valores si corresponde (esto es, si el paquete
            # está reconociendo el paquete al que está haciendo seguimiento).
            self.rto_estimator.process_ack(packet)
            # Luego, desencolar paquetes para retransmitir y parar/reiniciar
            # el timer de retransmisiones según corresponda.
            self.remove_from_retransmission_queue_packets_acked_by(packet)

    def remove_from_retransmission_queue_packets_acked_by(self, packet):
        # Sólo ACKs mayores que SND_UNA y menores que SND_NXT son válidos
        # en este punto.
        with self.rqueue:
            snd_una = self.control_block.get_snd_una()
            snd_nxt = self.control_block.get_snd_nxt()
            # Ver qué paquetes encolados son totalmente reconocidos por
            # este paquete. Necesitamos SND_UNA y SND_NXT para poder
            # comparar correctamente los SEQs y ACKs.
            removed_packets = self.rqueue.remove_acknowledged_by(
                packet, snd_una, snd_nxt)

            if len(removed_packets) > 0:
                # Algunos paquetes se reconocieron, por lo que debemos
                # actualizar el timer de retransmisiones.
                self.adjust_retransmission_timer()

    def adjust_retransmission_timer(self):
        # Comenzar con cero retransmisiones para el próximo paquete.
        self.retransmissions = 0
        if self.rqueue.empty():
            # Todos los datos salientes quedaron reconocidos. Detener el timer.
            self.retransmission_timer.stop()
        else:
            # Algunos datos se reconocieron pero otros aún quedan.
            # Reiniciar el timer usando la estimación actual del RTO.
            current_rto = self.rto_estimator.get_current_rto()
            self.retransmission_timer.restart(current_rto)

    def handle_outgoing(self):
        if self.control_block is None:
            # Cuando la conexión todavía no fue establecida, no tenemos nada
            # para enviar.
            return
        with self.control_block:
            # Analizar primero si tenemos un timeout de un paquete enviado.
            if self.retransmission_timer.has_expired() and\
               not self.rqueue.empty():
                # Si alcanzamos el máximo número permitido de retransmisiones,
                # liberar la conexión.
                if self.retransmissions >= MAX_RETRANSMISSION_ATTEMPTS:
                    self.free()
                    return
                # Limpiar la estimación del RTT si se hizo back-off reiteradas
                # veces. Posiblemente ya no represente el RTT real.
                if self.retransmissions > BOGUS_RTT_RETRANSMISSIONS:
                    self.rto_estimator.clear_rtt()
                self.retransmissions += 1
                # Hacer back-off del RTO y luego retransmitir el paquete más
                # viejo que aún no fue reconocido.
                self.rto_estimator.back_off_rto()
                packet = self.rqueue.head()
                self.send_and_queue(packet, is_retransmission=True)

            if self.write_stream_open or \
               self.control_block.has_data_to_send():
                self.attempt_to_send_data()
            else:
                # Mandar FIN cuando:
                #   * El stream de escritura está cerrado,
                #   * El estado es ESTABLISHED/CLOSE_WAIT
                #     (i.e., todavía no se ha enviado un FIN), y
                #   * Todo byte saliente fue exitosamente reconocido.
                self.attempt_to_send_FIN()

    def attempt_to_send_data(self):
        window_closed = False
        while self.control_block.has_data_to_send() and not window_closed:
            seq_number = self.control_block.get_snd_nxt()
            to_send = self.control_block.extract_from_out_buffer(MSS)
            if not to_send:
                # El bloque de control no devolvió nada, lo cual es un indicio
                # de que la ventana está cerrada. Luego, no tenemos nada más
                # por hacer hasta que lleguen los próximos ACKs.
                window_closed = True
            else:
                packet = self.build_packet(payload=to_send, seq=seq_number)
                self.send_and_queue(packet)

    def attempt_to_send_FIN(self):
        state_allows_closing = self.state in [ESTABLISHED, CLOSE_WAIT]
        if state_allows_closing and self.rqueue.empty():
            fin_packet = self.build_packet(flags=[ACKFlag, FINFlag])
            # Estamos enviando un FIN, y este flag se secuencia. Incrementar el
            # siguiente byte a enviar.
            self.control_block.increment_snd_nxt()
            new_state = FIN_WAIT1 if self.state == ESTABLISHED else LAST_ACK
            self.set_state(new_state)
            self.send_and_queue(fin_packet)

    def handle_incoming(self, packet):
        self.packet_handler.handle(packet)
        self.packet_sender.notify()

    def shutdown(self, how):

        if how == SHUT_RD:
            self.shutdown_read_stream()
        elif how == SHUT_WR:
            self.shutdown_write_stream()
        else:
            self.shutdown_read_stream()
            self.shutdown_write_stream()

    def shutdown_read_stream(self):
        self.read_stream_open = False

    def shutdown_write_stream(self):
        self.write_stream_open = False
        self.packet_sender.notify()

    def close(self, mode=NO_WAIT):
        #print 'lista rto: ' + str(self.rto_estimator.rtoList)

        self.close_mode = mode
        if self.state != CLOSED:
            self.shutdown(SHUT_RDWR)
            self.close_event.wait()
        self.free()
        self.join_threads()

    def free(self):
        self.printToFileRetransmission()
        #print str(self.rto_estimator.rtoList)
        if self.control_block is not None:
            self.control_block.flush_buffers()
        self.stop_threads()
        # En caso de que el establecimiento de conexión haya fallado, esto
        # destrabará al thread principal de la aplicación.
        self.connected_event.set()
        # Y, análogamente, esto destrabará al thread principal si se llama a
        # close y free es luego invocada por algún otro thread.
        self.close_event.set()
        self.set_state(CLOSED)

    def printToFileRetransmission(self):
        with open(self.filepath, 'a') as f:
            f.write(
                str(self.alpha) + ' ' + str(self.beta) + ' ' +
                str(self.retransmission_counter))
            f.write('\n')

    def printToFilePacketRTT(self):
        with open(self.filepath, 'a') as f:
            for (acknumber, rtt) in self.rto_estimator.rttList:
                print 'acknumber: ' + str(acknumber)
                print 'rtt: ' + str(rtt)
                f.write(str(acknumber) + ' ' + str(rtt * CLOCK_TICK))
                f.write('\n')