Ejemplo n.º 1
0
    def _create_packet(self):
        packet_size = 0
        packet_bytes = bytearray()

        sent_messages = []

        self._log.debug('%d packets pending' % len(self._outgoing))

        for message in self._outgoing:

            if message.require_ack:

                # a minimum resend delay is required for two reasons:
                # 1. deadlock with a 0ms latency connection causes _do_write
                #    to never exit as _create_packet always returns data.
                # 2. resending every 0ms is just plain stupid.

                t = time.time()
                resend_delay = max(self.MINIMUM_RESEND_DELAY_MS,
                                   self._transport_latency)

                self._log.debug('LSAT: %s' %
                                str(message.last_send_attempt_timestamp))
                self._log.debug('l8nc: %s' % str(self._transport_latency))
                self._log.debug('time: %s' % t)
                self._log.debug('rsnd: %s' % resend_delay)

                if message.last_send_attempt_timestamp is not None:
                    if ((message.last_send_attempt_timestamp + resend_delay) >=
                            t):
                        self._log.debug('Waiting for ack.')
                        continue

            if packet_size + message.length <= self.MTU:
                self._log.debug('Added data message into UDP packet')
                packet_size += message.length
                packet_bytes += message.message_bytes
                message.last_send_attempt_timestamp = time.time()
                sent_messages.append(message)
            else:
                self._log.debug('packet at MTU limit.')

        for sent_message in sent_messages:
            # Packets that require an ack are only removed
            # from the outgoing list if an ack is received.
            if not sent_message.require_ack:
                self._log.info('Message %d doesnt require ack - removing' %
                               sent_message.message_id)
                self._outgoing.remove(sent_message)
            else:
                self._log.info(
                    'Message %d requires ack - waiting for response' %
                    sent_message.message_id)

        return packet_bytes
Ejemplo n.º 2
0
    def __init__(self, parent=None, message_factory=None):
        if message_factory is None:
            self.message_factory = parent.message_factory
        else:
            self.message_factory = message_factory

        self.parent = parent
        self._last_receive_timestamp = time.time()
        self._last_send_timestamp = time.time()
        self._keep_alive_send_timestamp = time.time()
        self._keep_alive_message_id = 0

        # server: number of keepalives sent
        # client: number of keepalives received
        self._keepalive_count = 0

        self._ping_id = 0
        self._ping_send_timestamp = time.time()
        self._ping_meter = PingSampler()

        self.OnConnectRequestAccepted = Event()
        self.OnConnectRequestRejected = Event()
        self.OnConnectRequest = Event()
        self.OnError = Event()
        self.OnMessage = Event()
        self.OnDisconnect = Event()

        # Packet instances to be processed go in here
        self._incoming_messages = []

        # List of OutgoingMessages
        self._outgoing = []

        # In-order packet instances that have arrived early
        self._incoming_out_of_sequence_messages = []

        self._incoming_ordered_sequence_number = 0
        self._outgoing_ordered_sequence_number = 1
        self._outgoing_message_id = 0
        self._recent_message_ids = []

        # Metrics
        self._in_bytes = 0
        self._out_bytes = 0
        self._in_packets = 0
        self._out_packets = 0
        self._in_messages = 0
        self._out_messages = 0

        '''
        Default transport latency is high - This prevents spamming
        of the network prior to obtaining a calculated latency.
        '''
        self._transport_latency = 0.3 # 0.1 = 100ms
Ejemplo n.º 3
0
    def __init__(self, parent=None, message_factory=None):
        if message_factory is None:
            self.message_factory = parent.message_factory
        else:
            self.message_factory = message_factory

        self.parent = parent
        self._last_receive_timestamp = time.time()
        self._last_send_timestamp = time.time()
        self._keep_alive_send_timestamp = time.time()
        self._keep_alive_message_id = 0

        # server: number of keepalives sent
        # client: number of keepalives received
        self._keepalive_count = 0

        self._ping_id = 0
        self._ping_send_timestamp = time.time()
        self._ping_meter = PingSampler()

        self.OnConnectRequestAccepted = Event()
        self.OnConnectRequestRejected = Event()
        self.OnConnectRequest = Event()
        self.OnError = Event()
        self.OnMessage = Event()
        self.OnDisconnect = Event()

        # Packet instances to be processed go in here
        self._incoming_messages = []

        # List of OutgoingMessages
        self._outgoing = []

        # In-order packet instances that have arrived early
        self._incoming_out_of_sequence_messages = []

        self._incoming_ordered_sequence_number = 0
        self._outgoing_ordered_sequence_number = 1
        self._outgoing_message_id = 0
        self._recent_message_ids = []

        # Metrics
        self._in_bytes = 0
        self._out_bytes = 0
        self._in_packets = 0
        self._out_packets = 0
        self._in_messages = 0
        self._out_messages = 0
        '''
        Default transport latency is high - This prevents spamming
        of the network prior to obtaining a calculated latency.
        '''
        self._transport_latency = 0.3  # 0.1 = 100ms
Ejemplo n.º 4
0
    def _create_packet(self):
        packet_size = 0
        packet_bytes = bytearray()

        sent_messages = []

        self._log.debug('%d packets pending' % len(self._outgoing))

        for message in self._outgoing:

            if message.require_ack:

                # a minimum resend delay is required for two reasons:
                # 1. deadlock with a 0ms latency connection causes _do_write
                #    to never exit as _create_packet always returns data.
                # 2. resending every 0ms is just plain stupid.

                t = time.time()
                resend_delay = max(self.MINIMUM_RESEND_DELAY_MS, self._transport_latency)

                self._log.debug('LSAT: %s' % str(message.last_send_attempt_timestamp))
                self._log.debug('l8nc: %s' % str(self._transport_latency))
                self._log.debug('time: %s' % t)
                self._log.debug('rsnd: %s' % resend_delay)

                if message.last_send_attempt_timestamp is not None:
                    if ((message.last_send_attempt_timestamp +
                      resend_delay) >= t):
                        self._log.debug('Waiting for ack.')
                        continue

            if packet_size + message.length <= self.MTU:
                self._log.debug('Added data message into UDP packet')
                packet_size += message.length
                packet_bytes += message.message_bytes
                message.last_send_attempt_timestamp = time.time()
                sent_messages.append(message)
            else:
                self._log.debug('packet at MTU limit.')

        for sent_message in sent_messages:
            # Packets that require an ack are only removed
            # from the outgoing list if an ack is received.
            if not sent_message.require_ack:
                self._log.info('Message %d doesnt require ack - removing' %
                    sent_message.message_id)
                self._outgoing.remove(sent_message)
            else:
                self._log.info(
                    'Message %d requires ack - waiting for response' %
                    sent_message.message_id)

        return packet_bytes
Ejemplo n.º 5
0
    def _send_ping(self):
        self._ping_id += 1
        if (self._ping_id > netshared.USHRT_MAX):
            self._ping_id = 0

        ping = self.message_factory.get_by_name('Ping')()
        ping.id.value = self._ping_id
        self.send_message(ping)
        self._ping_send_timestamp = time.time()
Ejemplo n.º 6
0
    def _send_ping(self):
        self._ping_id += 1
        if (self._ping_id > netshared.USHRT_MAX):
            self._ping_id = 0

        ping = self.message_factory.get_by_name('Ping')()
        ping.id.value = self._ping_id
        self.send_message(ping)
        self._ping_send_timestamp = time.time()
Ejemplo n.º 7
0
    def _send_keep_alive(self):
        self._keep_alive_message_id += 1
        if (self._keep_alive_message_id > netshared.USHRT_MAX):
            self._keep_alive_message_id = 0

        message = self.message_factory.get_by_name('KeepAliveRequest')()
        message.id.value = self._keep_alive_message_id

        self.send_message(message)
        self._keep_alive_send_timestamp = time.time()
        self._keepalive_count += 1
Ejemplo n.º 8
0
    def _send_keep_alive(self):
        self._keep_alive_message_id += 1
        if (self._keep_alive_message_id > netshared.USHRT_MAX):
            self._keep_alive_message_id = 0

        message = self.message_factory.get_by_name('KeepAliveRequest')()
        message.id.value = self._keep_alive_message_id

        self.send_message(message)
        self._keep_alive_send_timestamp = time.time()
        self._keepalive_count += 1
Ejemplo n.º 9
0
    def __init__(self, parent=None, address=None):
        self._connected = False
        self.parent = parent
        self._socket = parent.socket
        self._address = address
        self._last_receive_timestamp = time.time()
        self._pending_disconnect = False

        self.OnConnectRequest = Event()
        self.OnDisconnect = Event()
        self.OnError = Event()
        self.OnMessage = Event()

        self._connection = Service('Connection', {'parent':self})

        self._connection.OnMessage += self._Connection_OnMessage
        self._connection.OnDisconnect += self._Connection_OnDisconnect
        self._connection.OnError += self._Connection_OnError
        self._connection.OnConnectRequest += self._Connection_OnConnectRequest
Ejemplo n.º 10
0
    def __init__(self, parent=None, address=None):
        self._connected = False
        self.parent = parent
        self._socket = parent.socket
        self._address = address
        self._last_receive_timestamp = time.time()
        self._pending_disconnect = False

        self.OnConnectRequest = Event()
        self.OnDisconnect = Event()
        self.OnError = Event()
        self.OnMessage = Event()

        self._connection = Service('Connection', {'parent': self})

        self._connection.OnMessage += self._Connection_OnMessage
        self._connection.OnDisconnect += self._Connection_OnDisconnect
        self._connection.OnError += self._Connection_OnError
        self._connection.OnConnectRequest += self._Connection_OnConnectRequest
Ejemplo n.º 11
0
    def send_message(self, message, ordered=False, reliable=False):
        '''
        Send a message and specify any options for the send method used.
        A message sent inOrder is implicitly sent as reliable.
        message is an instance of a subclass of packets.BasePacket.
        Returns the number of bytes added to the output queue for this
        message (header + message).
        '''
        self._last_send_timestamp = time.time()

        self._outgoing_message_id += 1
        message_id = self._outgoing_message_id
        if ordered:
            self._outgoing_ordered_sequence_number += 1
            inorder_sequence_number = self._outgoing_ordered_sequence_number
        else:
            inorder_sequence_number = 0

        packet_flags = bitfield()
        packet_flags[0] = int(ordered)
        packet_flags[1] = int(reliable)

        message_transport_header = struct.pack(
            '!'+self.MESSAGE_TRANSPORT_HEADER,
            message_id, inorder_sequence_number, int(packet_flags))

        message_bytes = message.get_packet_bytes()
        total_length = len(message_bytes)+len(message_transport_header)
        self._out_bytes += total_length

        self._add_message_bytes_to_output_list(
            message_id,
            message_transport_header+message_bytes,
            ordered or reliable)

        self._log.debug('Packet data length = %s' % len(message_bytes))
        self._log.debug('Header length = %s' % len(message_transport_header))
        self._log.debug('Added %d byte %s packet in outgoing buffer' %
            (total_length, message.__class__.__name__))

        return total_length
Ejemplo n.º 12
0
    def send_message(self, message, ordered=False, reliable=False):
        '''
        Send a message and specify any options for the send method used.
        A message sent inOrder is implicitly sent as reliable.
        message is an instance of a subclass of packets.BasePacket.
        Returns the number of bytes added to the output queue for this
        message (header + message).
        '''
        self._last_send_timestamp = time.time()

        self._outgoing_message_id += 1
        message_id = self._outgoing_message_id
        if ordered:
            self._outgoing_ordered_sequence_number += 1
            inorder_sequence_number = self._outgoing_ordered_sequence_number
        else:
            inorder_sequence_number = 0

        packet_flags = bitfield()
        packet_flags[0] = int(ordered)
        packet_flags[1] = int(reliable)

        message_transport_header = struct.pack(
            '!' + self.MESSAGE_TRANSPORT_HEADER, message_id,
            inorder_sequence_number, int(packet_flags))

        message_bytes = message.get_packet_bytes()
        total_length = len(message_bytes) + len(message_transport_header)
        self._out_bytes += total_length

        self._add_message_bytes_to_output_list(
            message_id, message_transport_header + message_bytes, ordered
            or reliable)

        self._log.debug('Packet data length = %s' % len(message_bytes))
        self._log.debug('Header length = %s' % len(message_transport_header))
        self._log.debug('Added %d byte %s packet in outgoing buffer' %
                        (total_length, message.__class__.__name__))

        return total_length
Ejemplo n.º 13
0
    def update(self):
        '''
        Send any packets that are in the output buffer and read
        any packets that have been received.
        '''
        try:
            self.parent.do_read(self._on_socket_data)
        except netshared.NetworkEndpointError:
            self.raiseOnError('Connection reset by peer')
            return

        if self._ping_meter.has_estimate():
            self._transport_latency = self._ping_meter.get_ping()

        read_messages = self._update(self.parent._socket, self.parent._address)

        if len(read_messages) != 0:
            self._last_receive_timestamp = time.time()

        for message in read_messages:

            if self.message_factory.is_a(message, 'ConnectRequestAccepted'):
                self.OnConnectRequestAccepted(self, None)

            elif self.message_factory.is_a(message, 'ConnectRequestRejected'):
                self.OnConnectRequestRejected(self, None)

            elif self.message_factory.is_a(message, 'KeepAliveResponse'):

                if (message.id.value == self._keep_alive_message_id):
                    self._ping_meter.add_sample(
                        (time.time() - self._keep_alive_send_timestamp) * 1000)
                else:
                    self._log.warning('Received old keep-alive, discarding')

            elif self.message_factory.is_a(message, 'KeepAliveRequest'):
                self._keepalive_count += 1
                response = self.message_factory.get_by_name(
                    'KeepAliveResponse')()
                response.id.value = message.id.value
                self.send_message(response)

            elif self.message_factory.is_a(message, 'Pong'):
                if (message.id.value == self._ping_id):
                    self._ping_meter.add_sample(
                        (time.time() - self._ping_send_timestamp) * 1000)
                else:
                    self._log.warning('Received old Pong, discarding')

            elif self.message_factory.is_a(message, 'Ping'):
                self._send_pong(message.id.value)

            elif self.message_factory.is_a(message, 'Disconnected'):
                self._log.debug('Received `Disconnected` message')
                self.OnDisconnect(self, None)

            elif self.message_factory.is_a(message, 'MessageAck'):
                self._process_message_ack(message.message_to_ack.value)

            elif self.message_factory.is_a(message, 'ConnectRequest'):
                # Unless the connection request is explicitly denied then
                # a connection is made - OnConnectRequest may return None
                # if no event handlers are bound.
                accept = True

                if (message.protocol.value != netshared.PROTOCOL_VERSION):
                    self._log.error('Invalid protocol version for client')
                    accept = False
                if self.OnConnectRequest(self.parent, message) is False:
                    accept = False

                if accept:
                    response = self.message_factory.get_by_name(
                        'ConnectRequestAccepted')
                    self.send_reliable_message(response())
                else:
                    response = self.message_factory.get_by_name(
                        'ConnectRequestRejected')
                    self.send_reliable_message(response())
                    self.pendingDisconnect = True
            else:
                self.OnMessage(self, message)

        if (time.time() > self._ping_send_timestamp + PING_REQUEST_FREQUENCY):
            if self.parent.is_server:
                self._keep_alive_send_timestamp = time.time()
            self._send_ping()

        if self.parent.is_server:
            # Server sends keep alive requests...
            if ((time.time() - self._keep_alive_send_timestamp) >
                (self.parent.timeout / 2)):
                self._send_keep_alive()
            # though it will eventually give up...
            if (time.time() -
                    self._last_receive_timestamp) > (self.parent.timeout):
                self.OnError(self, 'Connection timed out')
        else:
            # ...Client waits for the connection to timeout
            if (time.time() -
                    self._last_receive_timestamp) > (self.parent.timeout):
                self._log.info('Connection has timed out')
                self.OnError(self, 'Connection timed out')
Ejemplo n.º 14
0
    def update(self):
        '''
        Send any packets that are in the output buffer and read
        any packets that have been received.
        '''
        try:
            self.parent.do_read(self._on_socket_data)
        except netshared.NetworkEndpointError:
            self.raiseOnError('Connection reset by peer')
            return

        if self._ping_meter.has_estimate():
            self._transport_latency = self._ping_meter.get_ping()

        read_messages = self._update(
                        self.parent._socket, self.parent._address)

        if len(read_messages) != 0:
            self._last_receive_timestamp = time.time()

        for message in read_messages:

            if self.message_factory.is_a(message, 'ConnectRequestAccepted'):
                self.OnConnectRequestAccepted(self, None)

            elif self.message_factory.is_a(message, 'ConnectRequestRejected'):
                self.OnConnectRequestRejected(self, None)

            elif self.message_factory.is_a(message, 'KeepAliveResponse'):

                if (message.id.value == self._keep_alive_message_id):
                    self._ping_meter.add_sample(
                        (time.time()-self._keep_alive_send_timestamp)*1000)
                else:
                    self._log.warning('Received old keep-alive, discarding')

            elif self.message_factory.is_a(message, 'KeepAliveRequest'):
                self._keepalive_count += 1
                response = self.message_factory.get_by_name('KeepAliveResponse')()
                response.id.value = message.id.value
                self.send_message(response)

            elif self.message_factory.is_a(message, 'Pong'):
                if (message.id.value == self._ping_id):
                    self._ping_meter.add_sample(
                      (time.time()-self._ping_send_timestamp)*1000)
                else:
                    self._log.warning('Received old Pong, discarding')

            elif self.message_factory.is_a(message, 'Ping'):
                self._send_pong(message.id.value)

            elif self.message_factory.is_a(message, 'Disconnected'):
                self._log.debug('Received `Disconnected` message')
                self.OnDisconnect(self, None)

            elif self.message_factory.is_a(message, 'MessageAck'):
                self._process_message_ack(message.message_to_ack.value)

            elif self.message_factory.is_a(message, 'ConnectRequest'):
                # Unless the connection request is explicitly denied then
                # a connection is made - OnConnectRequest may return None
                # if no event handlers are bound.
                accept = True

                if (message.protocol.value != netshared.PROTOCOL_VERSION):
                    self._log.error('Invalid protocol version for client')
                    accept = False
                if self.OnConnectRequest(self.parent, message) is False:
                    accept = False

                if accept:
                    response = self.message_factory.get_by_name('ConnectRequestAccepted')
                    self.send_reliable_message(response())
                else:
                    response = self.message_factory.get_by_name('ConnectRequestRejected')
                    self.send_reliable_message(response())
                    self.pendingDisconnect = True
            else:
                self.OnMessage(self, message)


        if (time.time() > self._ping_send_timestamp + PING_REQUEST_FREQUENCY):
            if self.parent.is_server:
                self._keep_alive_send_timestamp = time.time()
            self._send_ping()


        if self.parent.is_server:
            # Server sends keep alive requests...
            if ((time.time()-self._keep_alive_send_timestamp)>
               (self.parent.timeout/2)):
                self._send_keep_alive()
            # though it will eventually give up...
            if (time.time()-self._last_receive_timestamp)>(self.parent.timeout):
                self.OnError(self, 'Connection timed out')
        else:
            # ...Client waits for the connection to timeout
            if (time.time()-self._last_receive_timestamp)>(self.parent.timeout):
                self._log.info('Connection has timed out')
                self.OnError(self, 'Connection timed out')