예제 #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
 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
예제 #3
0
파일: client.py 프로젝트: Laski/tdc
 def __init__(self, address, port):
     self.retransmission_queue = RetransmissionQueue(self)
     self.retransmission_attempts = dict()
     self.outgoing_buffer = DataBuffer()
     self.state = CLOSED
     self.control_block = ClientControlBlock(address, port)
     self.socket = Soquete(address, port)
     self.packet_builder = PacketBuilder(self)
예제 #4
0
 def __init__(self, delay=0, perdida=0):
     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.retransmission_attempts = dict()
     self.read_stream_open = True
     self.write_stream_open = True
     self.packet_handler = IncomingPacketHandler(self, delay, perdida)
     self.close_event = threading.Event()
     self.initialize_threads()
예제 #5
0
파일: client.py 프로젝트: belriose/Redes
	def __init__(self, address, port):
		self.retransmission_queue = RetransmissionQueue(self)
		self.retransmission_attempts = dict()
		self.outgoing_buffer = DataBuffer()
		self.state = CLOSED
		self.control_block = ClientControlBlock(address, port)
		self.socket = Soquete(address, port)
		self.packet_builder = PacketBuilder(self)
예제 #6
0
 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.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()
예제 #7
0
    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()
예제 #8
0
파일: protocol.py 프로젝트: lukius/ptc
 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()
예제 #9
0
    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()
예제 #10
0
파일: client.py 프로젝트: Bardammu/Redes
class PTCClientProtocol(object):
    
    def __init__(self, address, port):
        self.retransmission_queue = RetransmissionQueue(self)
        self.retransmission_attempts = dict()
        self.outgoing_buffer = DataBuffer()
        self.state = CLOSED
        self.control_block = ClientControlBlock(address, port)
        self.socket = Soquete(address, port)
        self.packet_builder = PacketBuilder(self)
    
    def is_connected(self):
        return self.state == ESTABLISHED
        
    def build_packet(self, payload=None, flags=None):
        seq = self.control_block.get_send_seq()
        if payload is not None:
            self.control_block.increment_send_seq()
        packet = self.packet_builder.build(payload=payload, flags=flags, seq=seq)
        return packet
        
    def send_packet(self, packet):
        self.socket.send(packet)
        
    def send_and_queue_packet(self, packet):
        self.send_packet(packet)
        self.retransmission_queue.put(packet)

        seq_number = packet.get_seq_number()
        if seq_number in self.retransmission_attempts:
            if self.retransmission_attempts[seq_number] > \
               MAX_RETRANSMISSION_ATTEMPTS + 1:
                self.retransmission_attempts[seq_number] = 0
        else:
            self.retransmission_attempts[seq_number] = 0
        
    def send(self, data):
        if not self.is_connected():
            raise Exception('cannot send data: connection not established')
        self.worker.send(data)

    def connect_to(self, address, port):
        self.worker = ClientProtocolWorker.spawn_for(self)
        self.worker.start()
        self.connected_event = threading.Event()
        self.control_block.set_destination_address(address)
        self.control_block.set_destination_port(port)
        
        syn_packet = self.build_packet(flags=[SYNFlag])
        self.send_and_queue_packet(syn_packet)
        self.state = SYN_SENT
        
        self.connected_event.wait()
    
    def handle_timeout(self):
        ###################
        ##   Completar!  ##
        ###################
        
        # Tener en cuenta que se debe:
        # (1) Obtener los paquetes en self.retranmission_queue
        # (2) Volver a enviarlos
        # (3) Reencolarlos para otra eventual retransmisión
        # ...y verificar que no se exceda la cantidad máxima de reenvíos!
        # (hacer self.shutdown() si esto ocurre y dejar un mensaje en self.error)

        packet_list = []
        for packet in self.retransmission_queue:
            packet_list.append(packet)

        self.retransmission_queue.clear()

        for packet in packet_list:

            if self.retransmission_attempts[packet.get_seq_number()] \
               <= MAX_RETRANSMISSION_ATTEMPTS:
                packet.add_flag(RSTFlag)    # Uso este flag sin utilidad para poder diferenciar retransmiciones
                self.send_and_queue_packet(packet)
                self.retransmission_attempts[packet.get_seq_number()] += 1   
            else:
                self.shutdown()
                self.error = 'Muchas retransmisiones, fuiste!'
                print "Muchas retransmisiones, fuiste!"

    

    def handle_pending_data(self):
        more_data_pending = False
        
        if self.control_block.send_allowed():
            try:
                data = self.outgoing_buffer.get(MIN_PACKET_SIZE, MAX_PACKET_SIZE)
            except NotEnoughDataException:
                pass
            else:
                packet = self.build_packet(payload=data)
                self.send_and_queue_packet(packet)
                
            if not self.outgoing_buffer.empty():
                more_data_pending = True
        else:
            more_data_pending = True
        
        if more_data_pending:
            self.worker.signal_pending_data()
    
    def handle_incoming(self, packet):
        ###################
        ##   Completar!  ##
        ###################
        
        # Tener en cuenta que se debe:
        # * Corroborar que el flag de ACK esté seteado
        # * Distinguir el caso donde el estado es SYN_SENT
        #   * No olvidar de hacer self.connected_event.set() al confirmar el ACK y establecer la conexión!!!
        # * Analizar si #ACK es aceptado (hablar con el bloque de control para hacer este checkeo)
        # * Sacar de la cola de retransmisión los paquetes reconocidos por #ACK
        # * Ajustar la ventana deslizante con #ACK
        # * Tener en cuenta también el caso donde el estado es FIN_SENT
        
        if ACKFlag in packet:
            ack_number = packet.get_ack_number()
            seq_number = packet.get_seq_number()

            if self.state == SYN_SENT:
                if ack_number == self.control_block.send_seq:
                    self.retransmission_queue.acknowledge(packet)
                    self.control_block.increment_send_seq()
                    self.control_block.update_send_window(ack_number)
                    self.state = ESTABLISHED
                    self.connected_event.set()

            elif self.state == FIN_SENT:
                if ack_number ==  self.control_block.send_seq:
                    self.retransmission_queue.acknowledge(packet)
                    self.state = CLOSED

            else:
                if self.control_block.ack_ok(ack_number):
                    self.retransmission_queue.acknowledge(packet)
                    self.control_block.update_send_window(ack_number)
            

    def handle_close_connection(self):
        if not self.outgoing_buffer.empty():
            self.worker.signal_pending_data()
            self.worker.signal_close_connection()
        elif not self.retransmission_queue.empty():
            self.worker.signal_close_connection()
        else:
            fin_packet = self.build_packet(flags=[FINFlag])
            self.send_and_queue_packet(fin_packet)
            self.state = FIN_SENT
        
    def close(self):
        if self.is_connected():
            self.worker.signal_close_connection()
        
    def shutdown(self):
        self.outgoing_buffer.clear()
        self.retransmission_queue.clear()
        self.retransmission_attempts.clear()
        self.worker.stop()
        # Esto es por si falló el establecimiento de conexión (para destrabar al thread principal)
        self.connected_event.set()
        self.state = CLOSED
예제 #11
0
파일: server.py 프로젝트: smgonzalez/redes
 def __init__(self, address, port):
     self.state = CLOSED
     self.control_block = ServerControlBlock(address, port)
     self.socket = Soquete(address, port)
     self.packet_builder = PacketBuilder(self)
     self.connection_closed_event = threading.Event()
예제 #12
0
파일: server.py 프로젝트: smgonzalez/redes
class PTCServerProtocol(object):
    
    def __init__(self, address, port):
        self.state = CLOSED
        self.control_block = ServerControlBlock(address, port)
        self.socket = Soquete(address, port)
        self.packet_builder = PacketBuilder(self)
        self.connection_closed_event = threading.Event()
    
    def is_connected(self):
        return self.state == ESTABLISHED
    
    def is_closed(self):
        return self.state == CLOSED
        
    def build_packet(self, flags=None):
        ack = self.control_block.get_receive_seq()
        packet = self.packet_builder.build(flags=flags, ack=ack)
        return packet      
        
    def send_packet(self, packet):
        self.socket.send(packet)
    
    def receive(self, size):
        if self.is_closed() and self.incoming_buffer.empty():
            raise Exception('cannot receive: connection ended and no more pending data buffered')
        elif self.incoming_buffer.empty() and not self.is_connected():
            raise Exception('cannot receive: connection not established')        
        if size < MIN_PACKET_SIZE:
            size = MIN_PACKET_SIZE
        data = self.incoming_buffer.sync_get(MIN_PACKET_SIZE, size)
        return data
    
    def accept(self):
        self.incoming_buffer = DataBuffer()
        self.state = CLOSED
        self.worker = ServerProtocolWorker.spawn_for(self)
        self.worker.start()
        # No hay mucho por hacer... simplemente esperar a que caiga el SYN del cliente
        self.connected_event = threading.Event()
        self.connected_event.wait()     
        
    def handle_incoming(self, packet):
        seq_number = packet.get_seq_number()
        if DEBUG: print 'recibi el paquete numero', seq_number
        if SYNFlag in packet:
            self.control_block.set_destination_address(packet.get_source_ip())
            self.control_block.set_destination_port(packet.get_source_port())
            self.control_block.set_receive_seq(seq_number)
            ack_packet = self.build_packet(flags=[ACKFlag])
            self.send_packet(ack_packet)
            self.control_block.set_receive_seq(seq_number)
            self.control_block.increment_receive_seq()            
            self.state = ESTABLISHED
            self.connected_event.set()
            if DEBUG: print 'server established'
        if FINFlag in packet:
            if self.control_block.accepts(seq_number):
                if DEBUG: print 'server ended'
                self.state = FIN_RECEIVED
                self.send_ack()
                self.close()
        else:
            data = packet.get_payload()
            if DEBUG: print 'recibí un paquete con esta data:', data
            if self.control_block.accepts(seq_number) and data:
                self.incoming_buffer.put(data)
                if self.state != FIN_RECEIVED:
                    self.send_ack()
                self.control_block.increment_receive_seq()
            
    def send_ack(self):
        ack_packet = self.build_packet(flags=[ACKFlag])
        self.send_packet(ack_packet)
        
    def shutdown(self):
        self.worker.stop()
        # Esto es por si falló el establecimiento de conexión (para destrabar al thread principal)
        self.connected_event.set()

    def close(self):
        self.state = CLOSED
        self.connection_closed_event.set()
        
    def wait_for_close(self):
        self.connection_closed_event.wait()
예제 #13
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)
예제 #14
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)
예제 #15
0
파일: client.py 프로젝트: belriose/Redes
class PTCClientProtocol(object):
	
	def __init__(self, address, port):
		self.retransmission_queue = RetransmissionQueue(self)
		self.retransmission_attempts = dict()
		self.outgoing_buffer = DataBuffer()
		self.state = CLOSED
		self.control_block = ClientControlBlock(address, port)
		self.socket = Soquete(address, port)
		self.packet_builder = PacketBuilder(self)
	
	def is_connected(self):
		return self.state == ESTABLISHED
		
	def build_packet(self, payload=None, flags=None):
		seq = self.control_block.get_send_seq()
		if payload is not None:
			self.control_block.increment_send_seq()
		packet = self.packet_builder.build(payload=payload, flags=flags, seq=seq)
		return packet
		
	def send_packet(self, packet):
		self.socket.send(packet)
		
	def send_and_queue_packet(self, packet):
		self.send_packet(packet)
		self.retransmission_queue.put(packet)
		
	def send(self, data):
		if not self.is_connected():
			raise Exception('cannot send data: connection not established')
		self.worker.send(data)

	def connect_to(self, address, port):
		self.worker = ClientProtocolWorker.spawn_for(self)
		self.worker.start()
		self.connected_event = threading.Event()
		self.control_block.set_destination_address(address)		# No tendria que ser el protocol block ?
		self.control_block.set_destination_port(port)		# No tendria que ser el protocol block ?
		
		syn_packet = self.build_packet(flags=[SYNFlag])
		self.send_and_queue_packet(syn_packet)
		self.state = SYN_SENT
		
		self.connected_event.wait()
	
	def handle_timeout(self):
		###################
		##   Completar!  ##
		###################
		
		# Tener en cuenta que se debe:
		# (1) Obtener los paquetes en self.retranmission_queue
		# (2) Volver a enviarlos
		# (3) Reencolarlos para otra eventual retransmisión
		# ...y verificar que no se exceda la cantidad máxima de reenvíos!
		# (hacer self.shutdown() si esto ocurre y dejar un mensaje en self.error)
		haypaquetesret = len(self.retransmission_queue.packets()) > 0
		if haypaquetesret:
			print "Retransmit"
			self.retransmission_queue.__iter__()
			primer_paquete = self.retransmission_queue.next().get_seq_number()
			if self.retransmission_attempts.get(primer_paquete,0) >= MAX_RETRANSMISSION_ATTEMPTS:
				self.error = "Error muchas retransmisiones"
				self.shutdown()
			else:
				copia_retransmission_queue = self.retransmission_queue.packets()
				self.retransmission_queue.clear()
				for pack in copia_retransmission_queue:
					self.send_and_queue_packet(pack)
					self.retransmission_attempts[pack.get_seq_number()] = self.retransmission_attempts.get(pack.get_seq_number(),0) + 1	# Le sumo uno a su cantidad de retransmisiones, inicilizandolo en cero si no existe
	
	def handle_pending_data(self):
		more_data_pending = False
		
		if self.control_block.send_allowed():
			try:
				data = self.outgoing_buffer.get(MIN_PACKET_SIZE, MAX_PACKET_SIZE)
			except NotEnoughDataException:
				pass
			else:
				packet = self.build_packet(payload=data)
				self.send_and_queue_packet(packet)
				
			if not self.outgoing_buffer.empty():
				more_data_pending = True
		else:
			more_data_pending = True
		
		if more_data_pending:
			self.worker.signal_pending_data()
	
	def handle_incoming(self, packet):
		###################
		##   Completar!  ##
		###################
		
		# Tener en cuenta que se debe:
		# * Corroborar que el flag de ACK esté seteado
		# * Distinguir el caso donde el estado es SYN_SENT
		#   * No olvidar de hacer self.connected_event.set() al confirmar el ACK y establecer la conexión!!!
		# * Analizar si #ACK es aceptado (hablar con el bloque de control para hacer este checkeo)
		# * Sacar de la cola de retransmisión los paquetes reconocidos por #ACK
		# * Ajustar la ventana deslizante con #ACK
		# * Tener en cuenta también el caso donde el estado es FIN_SENT
		ack_num = packet.get_ack_number()
		if ACKFlag in packet and self.control_block.valid_ack(ack_num):	# si hay ack y es valido
			self.retransmission_queue.acknowledge(packet)	# lo sacamos del retransmission queue
			self.control_block.update_window(ack_num)	# actualizamos la ventana
			for packs_ack in range(self.control_block.get_window_lo(),ack_num+1):	# Eliminamos los paquetes ackeados del diccionario de intentos de retransmision
				del retransmission_attempts[packs_ack]
			
			if self.state == SYN_SENT:	# Si estaba desconectado, conectate
				self.state = ESTABLISHED
				self.connected_event.set()
			elif self.state == FIN_SENT:	# Si te reconoce el fin de la conexion, terminala
				self.state = CLOSED
				self.retransmission_queue.clear()
				self.retransmission_attempts.clear()
				self.close()
			#~ if self.outgoing_buffer and self.retransmission_queue.empty():	#  Si recibimos el ack de lo ultimo imprimimos cuanto tardo
				#~ print clock() - start_time
			
	def handle_close_connection(self):
		if not self.outgoing_buffer.empty():
			self.worker.signal_pending_data()
			self.worker.signal_close_connection()
		elif not self.retransmission_queue.empty():
			self.worker.signal_close_connection()
		else:
			fin_packet = self.build_packet(flags=[FINFlag])
			self.send_and_queue_packet(fin_packet)
			self.state = FIN_SENT
		
	def close(self):
		if self.is_connected():
			self.worker.signal_close_connection()
		
	def shutdown(self):
		self.outgoing_buffer.clear()
		self.retransmission_queue.clear()
		self.retransmission_attempts.clear()
		self.worker.stop()
		# Esto es por si falló el establecimiento de conexión (para destrabar al thread principal)
		self.connected_event.set()
		self.state = CLOSED
예제 #16
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')
예제 #17
0
파일: client.py 프로젝트: crazy2k/tps-redes
class PTCClientProtocol(object):
    
    def __init__(self, address, port):
        self.retransmission_queue = RetransmissionQueue(self)
        self.retransmission_attempts = dict()
        self.outgoing_buffer = DataBuffer()
        self.state = CLOSED
        self.control_block = ClientControlBlock(address, port)
        self.socket = Soquete(address, port)
        self.packet_builder = PacketBuilder(self)
    
    def is_connected(self):
        return self.state == ESTABLISHED
        
    def build_packet(self, payload=None, flags=None):
        seq = self.control_block.get_send_seq()
        #Comente esto porque quedaba corrida la ventana de recepcion despues del connect
        #if payload is not None:
         #   self.control_block.increment_send_seq()
        self.control_block.increment_send_seq()
        packet = self.packet_builder.build(payload=payload, flags=flags, seq=seq)
        return packet
        
    def send_packet(self, packet):
        self.socket.send(packet)
        
    def send_and_queue_packet(self, packet):
        self.send_packet(packet)
        self.retransmission_queue.put(packet)
        
    def send(self, data):
        if not self.is_connected():
            raise Exception('cannot send data: connection not established')
        self.worker.send(data)

    def sendFile(self, nombre):
        if not self.is_connected():
            raise Exception('cannot send data: connection not established')
        archivo = open(nombre)
        line = archivo.readline() 
        self.worker.send(line)
        archivo.close()

    def connect_to(self, address, port):
        self.worker = ClientProtocolWorker.spawn_for(self)
        self.worker.start()
        self.connected_event = threading.Event()
        self.control_block.set_destination_address(address)
        self.control_block.set_destination_port(port)
        
        syn_packet = self.build_packet(flags=[SYNFlag])
        self.send_and_queue_packet(syn_packet)
        self.state = SYN_SENT
        
        self.connected_event.wait()
    
    def handle_timeout(self):
        ###################
        ##   Completar!  ##
        ###################
        
        # Tener en cuenta que se debe:
        # (1) Obtener los paquetes en self.retranmission_queue
        # (2) Volver a enviarlos
        # (3) Reencolarlos para otra eventual retransmisión
        # ...y verificar que no se exceda la cantidad máxima de reenvíos!
        # (hacer self.shutdown() si esto ocurre y dejar un mensaje en self.error)

        retransmited = RetransmissionQueue(self)
        shutdown = False
        for packet in self.retransmission_queue:
            seqNum = packet.get_seq_number()
            retransmitions = 0
            if(self.retransmission_attempts.has_key(seqNum)):
                retransmitions = self.retransmission_attempts[seqNum]

            if(retransmitions == MAX_RETRANSMISSION_ATTEMPTS):
                shutdown = True
                break
            else:
                self.send_packet(packet)
                retransmited.put(packet)
                self.retransmission_attempts[seqNum] = retransmitions + 1
        
        if(shutdown):
            self.shutdown()
        else:
            self.retransmission_queue = retransmited


    
    def handle_pending_data(self):
        more_data_pending = False
        
        if self.control_block.send_allowed():
            try:
                data = self.outgoing_buffer.get(MIN_PACKET_SIZE, MAX_PACKET_SIZE)
            except NotEnoughDataException:
                pass
            else:
                packet = self.build_packet(payload=data)
                self.send_and_queue_packet(packet)
                
            if not self.outgoing_buffer.empty():
                more_data_pending = True
        else:
            more_data_pending = True
        
        if more_data_pending:
            self.worker.signal_pending_data()
    
    def handle_incoming(self, packet):
        ###################
        ##   Completar!  ##
        ###################
        
        # Tener en cuenta que se debe:
        # * Corroborar que el flag de ACK esté seteado
        # * Distinguir el caso donde el estado es SYN_SENT
        #   * No olvidar de hacer self.connected_event.set() al confirmar el ACK y establecer la conexión!!!
        # * Analizar si #ACK es aceptado (hablar con el bloque de control para hacer este checkeo)
        # * Sacar de la cola de retransmisión los paquetes reconocidos por #ACK
        # * Ajustar la ventana deslizante con #ACK
        # * Tener en cuenta también el caso donde el estado es FIN_SENT
        
         if not ACKFlag in packet:
             print("NO ACK")
             return
         print("ACK "+str(packet.get_ack_number()))
         
         if not self.control_block.ack_valid(packet.get_ack_number()):
             print("NOT VALID ACK "+str(packet.get_ack_number()))
             return

         print("VALID ACK "+str(packet.get_ack_number()))
         self.retransmission_queue.acknowledge(packet)
         
         self.control_block.adjust_window(packet.get_ack_number())
         
         if(self.state == SYN_SENT):
             print("ESTABLISH CONNECTION")
             self.state = ESTABLISHED
             self.connected_event.set()
             return
        
         if(self.state == FIN_SENT):
             print("CLOSE CONNECTION")
             self.state = CLOSED
             self.close()

            
    def handle_close_connection(self):
        if not self.outgoing_buffer.empty():
            self.worker.signal_pending_data()
            self.worker.signal_close_connection()
        elif not self.retransmission_queue.empty():
            self.worker.signal_close_connection()
        else:
            fin_packet = self.build_packet(flags=[FINFlag])
            self.send_and_queue_packet(fin_packet)
            self.state = FIN_SENT
        
    def close(self):
        if self.is_connected():
            self.worker.signal_close_connection()
        
    def shutdown(self):
        self.outgoing_buffer.clear()
        self.retransmission_queue.clear()
        self.retransmission_attempts.clear()
        self.worker.stop()
        # Esto es por si falló el establecimiento de conexión (para destrabar al thread principal)
        self.connected_event.set()
        self.state = CLOSED
예제 #18
0
파일: client.py 프로젝트: smgonzalez/redes
class PTCClientProtocol(object):
    
    def __init__(self, address, port):
        self.retransmission_queue = RetransmissionQueue(self)
        self.retransmission_attempts = Counter()
        self.outgoing_buffer = DataBuffer()
        self.state = CLOSED
        self.control_block = ClientControlBlock(address, port)
        self.socket = Soquete(address, port)
        self.packet_builder = PacketBuilder(self)
    
    def is_connected(self):
        return self.state == ESTABLISHED
        
    def build_packet(self, payload=None, flags=None):
        seq = self.control_block.get_send_seq()
        if payload is not None:
            self.control_block.increment_send_seq()
        packet = self.packet_builder.build(payload=payload, flags=flags, seq=seq)
        return packet
        
    def send_packet(self, packet):
        if DEBUG and RANDOM_LOSS:
            #'perdemos' un 10% de los paquetes
            if random.choice(range(10)) :
                print 'mando el paquete:', packet.get_seq_number()
                self.socket.send(packet)
            else:
                print 'Mala leche con el', packet.get_seq_number()
        else:
            self.socket.send(packet)
        
    def send_and_queue_packet(self, packet):
        self.send_packet(packet)
        self.retransmission_queue.put(packet)
        
    def send(self, data):
        if not self.is_connected():
            raise Exception('cannot send data: connection not established')
        self.worker.send(data)

    def connect_to(self, address, port):
        self.worker = ClientProtocolWorker.spawn_for(self)
        self.worker.start()
        self.connected_event = threading.Event()
        self.control_block.reset_numbers()
        self.control_block.set_destination_address(address)
        self.control_block.set_destination_port(port)
        
        syn_packet = self.build_packet(flags=[SYNFlag])
        self.send_and_queue_packet(syn_packet)
        self.state = SYN_SENT
        
        self.connected_event.wait()
    
    def handle_timeout(self):
        ###################
        ##   Completar!  ##
        ###################
        
        # Tener en cuenta que se debe:
        # (1) Obtener los paquetes en self.retranmission_queue
        # (2) Volver a enviarlos
        # (3) Reencolarlos para otra eventual retransmisión
        # ...y verificar que no se exceda la cantidad máxima de reenvíos!
        # (hacer self.shutdown() si esto ocurre y dejar un mensaje en self.error)
        
        # *** NO SE SI NO HABIA YA OTRA VARIABLE DONDE SE INDICABA LA CANTIDAD DE TRANSMISIONES
        # *** SE CUENTA POR PAQUETE O LA COLA ENTERA?
        # *** HAY QUE RETRANSMITIR TODO LO QUE ESTA EN LA COLA?
        # Cantidad maxima de reenvios: MAX_RETRANSMISSION_ATTEMPTS
        if DEBUG: print 'TIMEOUT'
        oldQueue = self.retransmission_queue
        self.retransmission_queue = RetransmissionQueue(self)       
        for packet in oldQueue:
            if self.retransmission_attempts[packet.get_seq_number()] == MAX_RETRANSMISSION_ATTEMPTS :
                self.error = 'Retransmition Limit Exceeded'
                self.shutdown()
            else:
                self.retransmission_attempts[packet.get_seq_number()] += 1
                self.send_and_queue_packet(packet) 

    def handle_pending_data(self):
        more_data_pending = False
        
        if self.control_block.send_allowed():
            try:
                data = self.outgoing_buffer.get(MIN_PACKET_SIZE, MAX_PACKET_SIZE)
            except NotEnoughDataException:
                pass
            else:
                packet = self.build_packet(payload=data)
                self.send_and_queue_packet(packet)
                
            if not self.outgoing_buffer.empty():
                more_data_pending = True
        else:
            more_data_pending = True
        
        if more_data_pending:
            self.worker.signal_pending_data()
    
    
    def handle_incoming(self, packet):
        ###################
        ##   Completar!  ##
        ###################
        
        # Tener en cuenta que se debe:
        # * Corroborar que el flag de ACK esté seteado
        # * Distinguir el caso donde el estado es SYN_SENT
        #   * No olvidar de hacer self.connected_event.set() al confirmar el ACK y establecer la conexión!!!
        # * Analizar si #ACK es aceptado (hablar con el bloque de control para hacer este checkeo)
        # * Sacar de la cola de retransmisión los paquetes reconocidos por #ACK
        # * Ajustar la ventana deslizante con #ACK
        # * Tener en cuenta también el caso donde el estado es FIN_SENT
        
        # Corroboro que el flag de ACK este seteado
        if not ACKFlag in packet:
            raise Exception('Error (hay que hacer algo mas? Es un error?)')
            self.shutdown()
            return
            
        if DEBUG: print 'me llegó el paquete numero:', packet.get_ack_number()
        # Reviso si el ACK es aceptado
        if self.control_block.ack_accepted(packet): 
            # *** Si no es aceptado no hago nada no?
            for ackedPacket in self.retransmission_queue.acknowledge(packet):
                del(self.retransmission_attempts[ackedPacket.get_seq_number()])
            self.control_block.adjust_window(packet)
        else:
            return
 
        # Si fue aceptado, ejecuto accion en base al estado actual
        # *** ASUMO QUE NO SE LO LLAMA SI EL ESTADO ES CLOSED, ESO ESTARA BIEN?
        if self.state == ESTABLISHED:
            return
        elif self.state == SYN_SENT:
            # El servidor establecio una conexion: 
            # cambio el estado del cliente y sincronizo la conexion
            
            # Corroboro que el numero de ACK recibido sea igual al numero de SEQ enviado
            ackNum = packet.get_ack_number()
            seqNum = self.control_block.get_send_seq()
            
            if not (self.control_block.modular_increment(ackNum) == seqNum):
                #self.error = 'SYN_ACK inválido'
                #self.shutdown()
                if DEBUG: print 'recibí fruta (ack != seq):', ackNum, seqNum
                #no hagamos que se cierre, tal vez le llegó algo viejo o equivocado
                #dejemos que si posta anda mal, lo maten los timeouts.
                return
            
            self.state = ESTABLISHED
            self.connected_event.set()
            if DEBUG: print 'conexión establecida'
            
        elif self.state == FIN_SENT:
            # Recibi respuesta del servidor indicando que puedo cerrar la conexion
            if DEBUG: print 'llego el fin'
            self.state = CLOSED
            # *** Hago algo mas para cerrar la conexion?
            self.shutdown() #(?)
            
        else:
            self.error = 'Estado inexistente'
            self.shutdown()
            
    def handle_close_connection(self):
        if not self.outgoing_buffer.empty():
            self.worker.signal_pending_data()
            self.worker.signal_close_connection()
        elif not self.retransmission_queue.empty():
            self.worker.signal_close_connection()
        else:
            fin_packet = self.build_packet(flags=[FINFlag])
            self.send_and_queue_packet(fin_packet)
            self.state = FIN_SENT
        
    def close(self):
        if self.is_connected():
            self.worker.signal_close_connection()
        
    def shutdown(self):
        self.outgoing_buffer.clear()
        self.retransmission_queue.clear()
        self.retransmission_attempts.clear()
        self.worker.stop()
        # Esto es por si falló el establecimiento de conexión (para destrabar al thread principal)
        self.connected_event.set()
        self.state = CLOSED
예제 #19
0
파일: client.py 프로젝트: Laski/tdc
class PTCClientProtocol(object):
    def __init__(self, address, port):
        self.retransmission_queue = RetransmissionQueue(self)
        self.retransmission_attempts = dict()
        self.outgoing_buffer = DataBuffer()
        self.state = CLOSED
        self.control_block = ClientControlBlock(address, port)
        self.socket = Soquete(address, port)
        self.packet_builder = PacketBuilder(self)

    def is_connected(self):
        return self.state == ESTABLISHED

    def build_packet(self, payload=None, flags=None):
        seq = self.control_block.get_send_seq()
        #if payload is not None:
        self.control_block.increment_send_seq()
        packet = self.packet_builder.build(payload=payload,
                                           flags=flags,
                                           seq=seq)
        return packet

    def send_packet(self, packet):
        if (debug):
            print("Voy a mandar el paquete número: "),
            print(str(packet.get_seq_number()))
        #if random.randint(1, 11) == 1:
        # simulo congestión
        #    return
        self.socket.send(packet)

    def send_and_queue_packet(self, packet):
        self.send_packet(packet)
        self.retransmission_queue.put(packet)

    def send(self, data):
        if not self.is_connected():
            raise Exception('cannot send data: connection not established')
        self.worker.send(data)
        global t1
        t1 = time.time()

    def connect_to(self, address, port):
        self.worker = ClientProtocolWorker.spawn_for(self)
        self.worker.start()
        self.connected_event = threading.Event()
        self.control_block.set_destination_address(address)
        self.control_block.set_destination_port(port)

        syn_packet = self.build_packet(flags=[SYNFlag])
        self.send_and_queue_packet(syn_packet)
        self.state = SYN_SENT

        self.connected_event.wait()

    def handle_timeout(self):
        global count_rets
        count_rets += 1
        if (debug or True):
            print("Parece que se perdió un paquete, voy a reenviar.")
        new_queue = RetransmissionQueue(self)
        for packet in self.retransmission_queue:
            if packet not in self.retransmission_attempts:
                self.retransmission_attempts[packet.get_seq_number()] = 0
            self.retransmission_attempts[packet.get_seq_number()] += 1
            if self.retransmission_attempts[
                    packet.get_seq_number()] >= MAX_RETRANSMISSION_ATTEMPTS:
                self.shutdown()
                self.error = "Intentos de retransmisión superó el máximo"
                break
            else:
                self.send_packet(packet)
            new_queue.put(packet)
        self.retransmission_queue = new_queue

    def handle_pending_data(self):
        more_data_pending = False

        if self.control_block.send_allowed():
            try:
                data = self.outgoing_buffer.get(MIN_PACKET_SIZE,
                                                MAX_PACKET_SIZE)
            except NotEnoughDataException:
                pass
            else:
                packet = self.build_packet(payload=data)
                self.send_and_queue_packet(packet)

            if not self.outgoing_buffer.empty():
                more_data_pending = True
        else:
            more_data_pending = True

        if more_data_pending:
            self.worker.signal_pending_data()

    def handle_incoming(self, packet):
        if self.state == ESTABLISHED and self.control_block.accept_ack(packet):
            self.retransmission_queue.acknowledge(packet)
            self.clear_retransmission_attempts(packet.get_ack_number())
            if (debug):
                print("Recibí el ack número: "),
                print(str(packet.get_ack_number()))
            if self.outgoing_buffer.empty(
            ) and self.retransmission_queue.empty():
                print("Recibi el ultimo ack")
                print time.time() - t1
                global f, frets, count_rets

                f.write(str(time.time() - t1))
                f.write("\n")
                f.close()
                frets.write(str(count_rets))
                frets.write("\n")
                frets.close()
        elif self.state == SYN_SENT and self.control_block.accept_control_ack(
                packet):
            self.state = ESTABLISHED
            self.connected_event.set()
        elif self.state == FIN_SENT and self.control_block.accept_control_ack(
                packet):
            self.state = CLOSED

        # Tener en cuenta que se debe:
        # * Corroborar que el flag de ACK esté seteado
        # Se encarga el control_block
        # * Distinguir el caso donde el estado es SYN_SENT
        # Dale
        #   * No olvidar de hacer self.connected_event.set() al confirmar el ACK y establecer la conexión!!!
        # A full
        # * Analizar si #ACK es aceptado (hablar con el bloque de control para hacer este checkeo)
        # Sí
        # * Sacar de la cola de retransmisión los paquetes reconocidos por #ACK
        # Lo hace la cola
        # * Ajustar la ventana deslizante con #ACK
        # Lo hace el control_block
        # * Tener en cuenta también el caso donde el estado es FIN_SENT
        # Ok

    def clear_retransmission_attempts(self, ack):
        for seq_number in self.retransmission_attempts.keys():
            if seq_number <= ack:
                del self.retransmission_attempts[seq_number]

    def handle_close_connection(self):
        if not self.outgoing_buffer.empty():
            self.worker.signal_pending_data()
            self.worker.signal_close_connection()
        elif not self.retransmission_queue.empty():
            self.worker.signal_close_connection()
        else:
            fin_packet = self.build_packet(flags=[FINFlag])
            self.send_and_queue_packet(fin_packet)
            self.state = FIN_SENT

    def close(self):
        if self.is_connected():
            self.worker.signal_close_connection()

    def shutdown(self):
        self.outgoing_buffer.clear()
        self.retransmission_queue.clear()
        self.retransmission_attempts.clear()
        self.worker.stop()
        # Esto es por si falló el establecimiento de conexión (para destrabar al thread principal)
        self.connected_event.set()
        self.state = CLOSED
예제 #20
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)
예제 #21
0
파일: client.py 프로젝트: Laski/tdc
class PTCClientProtocol(object):
    
    def __init__(self, address, port):
        self.retransmission_queue = RetransmissionQueue(self)
        self.retransmission_attempts = dict()
        self.outgoing_buffer = DataBuffer()
        self.state = CLOSED
        self.control_block = ClientControlBlock(address, port)
        self.socket = Soquete(address, port)
        self.packet_builder = PacketBuilder(self)
    
    def is_connected(self):
        return self.state == ESTABLISHED
        
    def build_packet(self, payload=None, flags=None):
        seq = self.control_block.get_send_seq()
        #if payload is not None:
        self.control_block.increment_send_seq()
        packet = self.packet_builder.build(payload=payload, flags=flags, seq=seq)
        return packet
        
    def send_packet(self, packet):
        if(debug):
            print("Voy a mandar el paquete número: "),
            print(str(packet.get_seq_number()))
        #if random.randint(1, 11) == 1:
            # simulo congestión
        #    return
        self.socket.send(packet)
        
    def send_and_queue_packet(self, packet):
        self.send_packet(packet)
        self.retransmission_queue.put(packet)
        
    def send(self, data):
        if not self.is_connected():
            raise Exception('cannot send data: connection not established')
        self.worker.send(data)
        global t1
        t1 = time.time()

    def connect_to(self, address, port):
        self.worker = ClientProtocolWorker.spawn_for(self)
        self.worker.start()
        self.connected_event = threading.Event()
        self.control_block.set_destination_address(address)
        self.control_block.set_destination_port(port)
        
        syn_packet = self.build_packet(flags=[SYNFlag])
        self.send_and_queue_packet(syn_packet)
        self.state = SYN_SENT
        
        self.connected_event.wait()
    
    def handle_timeout(self):
        global count_rets
        count_rets += 1
        if(debug or True):
            print ("Parece que se perdió un paquete, voy a reenviar.")
        new_queue = RetransmissionQueue(self)
        for packet in self.retransmission_queue:
            if packet not in self.retransmission_attempts:
                self.retransmission_attempts[packet.get_seq_number()] = 0
            self.retransmission_attempts[packet.get_seq_number()] += 1
            if self.retransmission_attempts[packet.get_seq_number()] >= MAX_RETRANSMISSION_ATTEMPTS:
                self.shutdown()
                self.error =  "Intentos de retransmisión superó el máximo"
                break
            else:
                self.send_packet(packet)
            new_queue.put(packet)
        self.retransmission_queue = new_queue
        
    def handle_pending_data(self):
        more_data_pending = False
        
        if self.control_block.send_allowed():
            try:
                data = self.outgoing_buffer.get(MIN_PACKET_SIZE, MAX_PACKET_SIZE)
            except NotEnoughDataException:
                pass
            else:
                packet = self.build_packet(payload=data)
                self.send_and_queue_packet(packet)
                
            if not self.outgoing_buffer.empty():
                more_data_pending = True
        else:
            more_data_pending = True
        
        if more_data_pending:
            self.worker.signal_pending_data()
    
    def handle_incoming(self, packet):
        if self.state == ESTABLISHED and self.control_block.accept_ack(packet):
            self.retransmission_queue.acknowledge(packet)
            self.clear_retransmission_attempts(packet.get_ack_number())
            if(debug):
                print("Recibí el ack número: "),
                print(str(packet.get_ack_number()))
            if self.outgoing_buffer.empty() and self.retransmission_queue.empty():
                print("Recibi el ultimo ack")
                print time.time() - t1
                global f, frets, count_rets

                f.write(str(time.time() - t1))
                f.write("\n")
                f.close()
                frets.write(str(count_rets))
                frets.write("\n")
                frets.close()
        elif self.state == SYN_SENT and self.control_block.accept_control_ack(packet):
            self.state = ESTABLISHED
            self.connected_event.set()
        elif self.state == FIN_SENT and self.control_block.accept_control_ack(packet):
                self.state = CLOSED
                        
        # Tener en cuenta que se debe:
        # * Corroborar que el flag de ACK esté seteado
            # Se encarga el control_block
        # * Distinguir el caso donde el estado es SYN_SENT
            # Dale
        #   * No olvidar de hacer self.connected_event.set() al confirmar el ACK y establecer la conexión!!!
            # A full
        # * Analizar si #ACK es aceptado (hablar con el bloque de control para hacer este checkeo)
            # Sí
        # * Sacar de la cola de retransmisión los paquetes reconocidos por #ACK
            # Lo hace la cola
        # * Ajustar la ventana deslizante con #ACK
            # Lo hace el control_block
        # * Tener en cuenta también el caso donde el estado es FIN_SENT
            # Ok
            
    def clear_retransmission_attempts(self, ack):
        for seq_number in self.retransmission_attempts.keys():
            if seq_number <= ack:
                del self.retransmission_attempts[seq_number]
            
    def handle_close_connection(self):
        if not self.outgoing_buffer.empty():
            self.worker.signal_pending_data()
            self.worker.signal_close_connection()
        elif not self.retransmission_queue.empty():
            self.worker.signal_close_connection()
        else:
            fin_packet = self.build_packet(flags=[FINFlag])
            self.send_and_queue_packet(fin_packet)
            self.state = FIN_SENT
        
    def close(self):
        if self.is_connected():
            self.worker.signal_close_connection()
        
    def shutdown(self):
        self.outgoing_buffer.clear()
        self.retransmission_queue.clear()
        self.retransmission_attempts.clear()
        self.worker.stop()
        # Esto es por si falló el establecimiento de conexión (para destrabar al thread principal)
        self.connected_event.set()
        self.state = CLOSED
예제 #22
0
파일: server.py 프로젝트: Laski/tdc
 def __init__(self, address, port):
     self.state = CLOSED
     self.control_block = ServerControlBlock(address, port)
     self.socket = Soquete(address, port)
     self.packet_builder = PacketBuilder(self)
     self.connection_closed_event = threading.Event()
예제 #23
0
파일: server.py 프로젝트: Laski/tdc
class PTCServerProtocol(object):
    
    def __init__(self, address, port):
        self.state = CLOSED
        self.control_block = ServerControlBlock(address, port)
        self.socket = Soquete(address, port)
        self.packet_builder = PacketBuilder(self)
        self.connection_closed_event = threading.Event()
    
    def is_connected(self):
        return self.state == ESTABLISHED
    
    def is_closed(self):
        return self.state == CLOSED
        
    def build_packet(self, flags=None):
        ack = self.control_block.get_receive_seq()
        packet = self.packet_builder.build(flags=flags, ack=ack)
        return packet      
        
    def send_packet(self, packet):
        self.socket.send(packet)
    
    def receive(self, size):
        if self.is_closed() and self.incoming_buffer.empty():
            raise Exception('cannot receive: connection ended and no more pending data buffered')
        elif self.incoming_buffer.empty() and not self.is_connected():
            raise Exception('cannot receive: connection not established')        
        if size < MIN_PACKET_SIZE:
            size = MIN_PACKET_SIZE
        data = self.incoming_buffer.sync_get(MIN_PACKET_SIZE, size)
        return data
    
    def accept(self):
        self.incoming_buffer = DataBuffer()
        self.state = CLOSED
        self.worker = ServerProtocolWorker.spawn_for(self)
        self.worker.start()
        # No hay mucho por hacer... simplemente esperar a que caiga el SYN del cliente
        self.connected_event = threading.Event()
        self.connected_event.wait()     

    def handle_incoming(self, packet):
        seq_number = packet.get_seq_number()
        if(debug):
			print ("Recibi el paquete numero: "),
			print (seq_number)
        if SYNFlag in packet:
            self.control_block.set_destination_address(packet.get_source_ip())
            self.control_block.set_destination_port(packet.get_source_port())
            self.control_block.set_receive_seq(seq_number)
            ack_packet = self.build_packet(flags=[ACKFlag])
            self.send_packet(ack_packet)
            self.control_block.set_receive_seq(seq_number)
            self.control_block.increment_receive_seq()            
            self.state = ESTABLISHED
            self.connected_event.set()
        if FINFlag in packet:
            if self.control_block.accepts(seq_number):
                self.state = FIN_RECEIVED
                self.send_ack()
                self.close()
        else:
            data = packet.get_payload()
            if self.control_block.accepts(seq_number) and data:
                self.incoming_buffer.put(data)
                if self.state != FIN_RECEIVED:
                    self.send_ack()
                self.control_block.increment_receive_seq()
            
    def send_ack(self):
        ack_packet = self.build_packet(flags=[ACKFlag])
        self.send_packet(ack_packet)
        
    def shutdown(self):
        self.worker.stop()
        # Esto es por si falló el establecimiento de conexión (para destrabar al thread principal)
        self.connected_event.set()

    def close(self):
        self.state = CLOSED
        self.connection_closed_event.set()
        
    def wait_for_close(self):
        self.connection_closed_event.wait()