예제 #1
0
    def connect(self, address):
        '''
        Initiate a connection to the server at the specified address.

        This method will put the socket into the `CONNECTING` state. If a
        connection is already established a ClientError exception is raised.

        :Parameters:
            address : (host, port)
                Host address. An ArgumentError exception will be raised for
                an invalid address.
        '''
        if self.is_active():
            raise ClientError(
                'Client cannot reconnect in a CONNECTING or CONNECTED state')
        if len(address) != 2:
            raise ArgumentError('Expected parameter is (host, port) tuple')

        host = address[0]

        try:
            port = int(address[1])
        except ValueError:
            raise ArgumentError('%s is not a valid port' % address[1])
        if port > 65535 or port < 1:
            raise ArgumentError('%s is not a valid port' % port)

        #port = 6385
        self._create_socket()
        self._connect_socket((host, port))
        self._address = (host, port)

        self._connection = Service('Connection', {'parent': self})
        self._connection.OnConnectRequestAccepted += \
            self._Connection_OnConnectRequestAccepted
        self._connection.OnConnectRequestRejected += \
            self._Connection_OnConnectRequestRejected
        self._connection.OnError += self._Connection_OnError
        self._connection.OnDisconnect += self._Connection_OnDisconnect
        self._connection.OnMessage += self._Connection_OnMessage

        request_message = self.message_factory.get_by_name('ConnectRequest')()
        request_message.protocol.value = netshared.PROTOCOL_VERSION
        self._send_reliable_message(request_message)
        self._state = self.CONNECTING
예제 #2
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
예제 #3
0
파일: server.py 프로젝트: Ohmnivore/Frenzy
    def _on_socket_data(self, data, addr):
        self._log.debug('Got data %s bytes in length from %s' %
                        (str(len(data)), str(addr)))

        if not addr in self._peers and not self._accept_new_connections:
            return

        if not addr in self._peers:
            new_peer = Service('Peer', {'parent': self, 'address': addr})
            self._peers[addr] = new_peer

            new_peer.OnDisconnect += self._Peer_OnDisconnect
            new_peer.OnError += self._Peer_OnError
            new_peer.OnMessage += self._Peer_OnMessage
            new_peer.OnConnectRequest += self._Peer_OnConnectRequest

        self._peers[addr].process_inbound_packet(data)
예제 #4
0
파일: client.py 프로젝트: Ohmnivore/Frenzy
    def connect(self, address):
        '''
        Initiate a connection to the server at the specified address.

        This method will put the socket into the `CONNECTING` state. If a
        connection is already established a ClientError exception is raised.

        :Parameters:
            address : (host, port)
                Host address. An ArgumentError exception will be raised for
                an invalid address.
        '''
        if self.is_active():
            raise ClientError(
                'Client cannot reconnect in a CONNECTING or CONNECTED state')
        if len(address) != 2:
            raise ArgumentError('Expected parameter is (host, port) tuple')

        host = address[0]

        try:
            port = int(address[1])
        except ValueError:
            raise ArgumentError('%s is not a valid port' % address[1])
        if port > 65535 or port < 1:
            raise ArgumentError('%s is not a valid port' % port)

        #port = 6385
        self._create_socket()
        self._connect_socket((host, port))
        self._address = (host, port)

        self._connection = Service('Connection', {'parent':self})
        self._connection.OnConnectRequestAccepted += \
            self._Connection_OnConnectRequestAccepted
        self._connection.OnConnectRequestRejected += \
            self._Connection_OnConnectRequestRejected
        self._connection.OnError += self._Connection_OnError
        self._connection.OnDisconnect += self._Connection_OnDisconnect
        self._connection.OnMessage += self._Connection_OnMessage

        request_message = self.message_factory.get_by_name('ConnectRequest')()
        request_message.protocol.value = netshared.PROTOCOL_VERSION
        self._send_reliable_message(request_message)
        self._state = self.CONNECTING
예제 #5
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
예제 #6
0
class Peer(metrics.Metrics):
    '''
    A connection to the server. Each connected Client has a paired
    Peer object instance on the server.
    '''
    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

    @property
    def address(self):
        return self._address

    @property
    def connected(self):
        return self._connected

    @property
    def in_bytes(self):
        return self._connection.in_bytes

    @property
    def is_server(self):
        return True

    @property
    def latency(self):
        return self._connection.latency

    @property
    def message_factory(self):
        return self.parent.message_factory

    @property
    def out_buffer_bytes(self):
        return self._connection.out_buffer_bytes

    @property
    def out_bytes(self):
        return self._connection.out_bytes

    @property
    def timeout(self):
        return self.parent.timeout

    @property
    def last_packet_sent_at(self):
        return self._connection.last_packet_sent_at

    def _Connection_OnMessage(self, sender, event_args):
        if not self._connected:
            self._pending_disconnect = True
        else:
            self.OnMessage(self, event_args)

    def _Connection_OnConnectRequest(self, sender, event_args):
        self._connected = self.OnConnectRequest(self, event_args)
        if self._connected is None:
            # No event handler bound, default action is to accept connection.
            self._connected = True
        return self._connected

    def _Connection_OnError(self, sender, error_string):
        self.OnError(self, error_string)

    def _Connection_OnDisconnect(self, sender, event_args):
        self.OnDisconnect(self, sender)

    def do_read(self, callback):
        self.parent.do_read(callback)

    def process_inbound_packet(self, rawData):
        self._connection.process_inbound_packet(rawData)

    def has_packets_to_send(self):
        return self._connection.has_outgoing_packets()

    def send_message(self, packet):
        '''
        Adds a packet to the outgoing buffer to be sent to the client.
        This does not set the in-order or reliable flags.
        packet is an instance of BasePacket.
        Returns the number of bytes added to the output buffer for
        sending this message (header + message bytes)
        '''
        if self._pending_disconnect:
            raise netshared.ServerError('Cannot send_message to a disconnecting peer')
        return self._connection.send_message(packet)

    def send_reliable_message(self, packet):
        if self._pending_disconnect:
            raise netshared.ServerError('Cannot send_reliable_message to a disconnecting peer')
        self._connection.send_reliable_message(packet)

    def disconnect(self):
        '''
        Disconnect this peer. A Disconnected message will be sent to the client
        and this peer object will be deleted once the outgoing buffer for
        this connection has emptied.
        '''
        LOG.info('Sent Disconnected message to client')
        self._connection.send_message(
            self.parent.message_factory.get_by_name('Disconnected')())
        self._pending_disconnect = True

    def update(self):
        self._connection.update()
예제 #7
0
파일: client.py 프로젝트: Ohmnivore/Frenzy
class Client(netshared.NetworkEndpoint, metrics.Metrics):
    '''A `Client` manages the connection to a `Server` instance elsewhere.

    Creating an instance of a `Client` and connecting to a server is done
    as shown in the minimalist example below::

        client = Client()
        # Server is running on localhost port 9000
        client.connect(('localhost', 9000))

        # This loop ensures that .update() is called.
        while True:
            client.update()
            # Add a small time delay to prevent pegging the CPU.
            time.sleep(0.0001)

    The `Client` has a number of events that can be hooked into that provide
    notifications of data sent from the server and state changes. An event
    consists of the sender and the argument(in the example below, this
    is the message), eg::

        def my_message_handler(sender, message):
            print("The greeting reads: %s" % message.greeting.value)

        my_client.OnMessage += my_message_handler

    For the `Client.OnMessage` handler example above the argument part of the
    event received is a re-assembled instance of the message that was sent, and
    the greeting field in the message is obtained via
    the fields `value` attribute.

    * `Client.OnConnectRequestAccepted` - Fired when a `Client.connect` request
        has been responded to by the server allowing the connection.
    * `Client.OnConnectRequestRejected` - Fired when a `Client.connect` request
        has been responded to by the server deneying the connection.
    * `Client.OnMessage` - Fired when a message is receieved from the server.
        See above example.
    * `Client.OnError` - An error has occured. The event argument is a string
        detailing the error.
    * `Client.OnDisconnect` - The connection was gracefully closed by the
        Server. If the connection was severed due to a time-out, the
        `Client.OnError` event would fire.
    '''

    _log = logging.getLogger('legume.client')

    def __init__(self, message_factory=messages.message_factory):
        '''
        Create a Client endpoint. A Client is initially in the closed state
        until a call to `connect`.

        A messages factory is required to assemble and disassemble messages for
        pushing down the intertubes to the server endpoint. If a
        message_factory is not explicitly specified then the global
        message_factory will be used.

        :Parameters:
            message_factory : `MessageFactory`
                A message factory.
        '''
        netshared.NetworkEndpoint.__init__(self, message_factory)
        self._address = None
        self._connection = None
        self._disconnecting = False

        self._OnConnectRequestRejected = Event()
        self._OnMessage = Event()
        self._OnConnectRequestAccepted = Event()
        self._OnError = Event()
        self._OnDisconnect = Event()

    # ------------- Properties -------------

    @property
    def connected(self):
        '''
        Returns True if this endpoint's state is `CONNECTED`.
        '''
        return self._state == self.CONNECTED

    @property
    def disconnected(self):
        '''
        Returns True if this endpoint's state is `DISCONNECTED`.
        '''
        return self._state == self.DISCONNECTED

    @property
    def errored(self):
        '''
        Returns True if this endpoint's state is `ERRORED`.
        '''
        return self._state == self.ERRORED

    @property
    def is_server(self):
        '''Returns false.'''
        return False

    @property
    def latency(self):
        if self._connection is not None:
            return self._connection.latency
        else:
            return 0

    @property
    def out_buffer_bytes(self):
        if self._connection is not None:
            return self._connection.out_buffer_bytes
        else:
            return 0

    @property
    def pending_acks(self):
        # TODO: implement
        return 0

    @property
    def in_bytes(self):
        if self._connection is not None:
            return self._connection.in_bytes
        else:
            return 0

    @property
    def out_bytes(self):
        if self._connection is not None:
            return self._connection.out_bytes
        else:
            return 0

    @property
    def in_packets(self):
        # TODO: implement
        return 0

    @property
    def out_packets(self):
        # TODO: implement
        return 0

    @property
    def keepalive_count(self):
        if self._connection is not None:
            return self._connection.keepalive_count
        else:
            return 0

    @property
    def reorder_queue(self):
        if self._connection is not None:
            return self._connection.reorder_queue
        else:
            return 0

    # ------------- Public Methods -------------

    def connect(self, address):
        '''
        Initiate a connection to the server at the specified address.

        This method will put the socket into the `CONNECTING` state. If a
        connection is already established a ClientError exception is raised.

        :Parameters:
            address : (host, port)
                Host address. An ArgumentError exception will be raised for
                an invalid address.
        '''
        if self.is_active():
            raise ClientError(
                'Client cannot reconnect in a CONNECTING or CONNECTED state')
        if len(address) != 2:
            raise ArgumentError('Expected parameter is (host, port) tuple')

        host = address[0]

        try:
            port = int(address[1])
        except ValueError:
            raise ArgumentError('%s is not a valid port' % address[1])
        if port > 65535 or port < 1:
            raise ArgumentError('%s is not a valid port' % port)

        #port = 6385
        self._create_socket()
        self._connect_socket((host, port))
        self._address = (host, port)

        self._connection = Service('Connection', {'parent':self})
        self._connection.OnConnectRequestAccepted += \
            self._Connection_OnConnectRequestAccepted
        self._connection.OnConnectRequestRejected += \
            self._Connection_OnConnectRequestRejected
        self._connection.OnError += self._Connection_OnError
        self._connection.OnDisconnect += self._Connection_OnDisconnect
        self._connection.OnMessage += self._Connection_OnMessage

        request_message = self.message_factory.get_by_name('ConnectRequest')()
        request_message.protocol.value = netshared.PROTOCOL_VERSION
        self._send_reliable_message(request_message)
        self._state = self.CONNECTING

    def disconnect(self):
        '''
        Gracefully disconnect from the host. A disconnection packet is
        sent to the server upon calling the .update() method. The connection
        status of the class instance will not changed to  `DISCONNECTED`
        until .update() is called.
        '''
        if self._connection is not None:
            self._connection.send_message(
                self.message_factory.get_by_name('Disconnected')())
            self._disconnecting = True

    def send_message(self, message):
        '''
        Send a message to the server. The message is added to the output buffer.
        To flush the output buffer call the .update() method. If the client
        is not connected to the server a `ClientError` exception is raised.

        :Parameters:
            message : `BaseMessage`
                The message to be sent
        '''
        if self._state == self.CONNECTED:
            return self._send_message(message)
        else:
            raise ClientError('Cannot send packet - not connected')

    def send_reliable_message(self, message):
        '''
        Send a message to the server with guaranteed delivery. If the
        client is not connected to the server a `ClientError` exception
        is raised.

        :Parameters:
            message : `BaseMessage`
                The message to be sent
        '''
        if self._state == self.CONNECTED:
            return self._send_reliable_message(message)
        else:
            raise ClientError('Cannot send message - not connected')

    def update(self):
        '''
        This method should be called frequently to process incoming data,
        send outgoing data, and raise events.
        '''
        self._log.debug('update')
        if self._state in [self.CONNECTING, self.CONNECTED]:
            self._log.debug('connection.update')
            self._connection.update()

        if self._disconnecting and not self._connection.has_outgoing_packets():
            self._disconnect(raise_event=False)

    # ------------- Private Methods -------------

    def _send_message(self, message):
        return self._connection.send_message(message)

    def _send_reliable_message(self, message):
        return self._connection.send_reliable_message(message)

    def _disconnect(self, raise_event=True):
        self._state = self.DISCONNECTED
        self._shutdown_socket()
        self._disconnecting = False
        if raise_event:
            self.OnDisconnect(self, None)

    # ------------- Events -------------

    def _getOnConnectRequestRejected(self):
        return self._OnConnectRequestRejected
    def _setOnConnectRequestRejected(self, event):
        if isinstance(event, Event):
            self._OnConnectRequestRejected = event
        else:
            raise NEventError('Event must subclass nevent.Event')
    OnConnectRequestRejected = property(
        _getOnConnectRequestRejected, _setOnConnectRequestRejected)

    def _getOnMessage(self):
        return self._OnMessage
    def _setOnMessage(self, event):
        if isinstance(event, Event):
            self._OnMessage = event
        else:
            raise NEventError('Event must subclass nevent.Event')
    OnMessage = property(
        _getOnMessage, _setOnMessage)

    def _getOnConnectRequestAccepted(self):
        return self._OnConnectRequestAccepted
    def _setOnConnectRequestAccepted(self, event):
        if isinstance(event, Event):
            self._OnConnectRequestAccepted = event
        else:
            raise NEventError('Event must subclass nevent.Event')
    OnConnectRequestAccepted = property(
        _getOnConnectRequestAccepted, _setOnConnectRequestAccepted)

    def _getOnError(self):
        return self._OnError
    def _setOnError(self, event):
        if isinstance(event, Event):
            self._OnError = event
        else:
            raise NEventError('Event must subclass nevent.Event')
    OnError = property(
        _getOnError, _setOnError)

    def _getOnDisconnect(self):
        return self._OnDisconnect
    def _setOnDisconnect(self, event):
        if isinstance(event, Event):
            self._OnDisconnect = event
        else:
            raise NEventError('Event must subclass nevent.Event')
    OnDisconnect = property(
        _getOnDisconnect, _setOnDisconnect)

    # ------------- Connection Event Handlers -------------

    def _Connection_OnConnectRequestRejected(self, sender, event_args):
        self._state = self.ERRORED
        self._shutdown_socket()
        self.OnConnectRequestRejected(self, event_args)

    def _Connection_OnMessage(self, sender, message):
        self.OnMessage(self, message)

    def _Connection_OnConnectRequestAccepted(self, sender, event_args):
        self._state = self.CONNECTED
        self.OnConnectRequestAccepted(self, event_args)

    def _Connection_OnError(self, sender, error_string):
        self._state = self.ERRORED
        self._shutdown_socket()
        self.OnError(self, error_string)

    def _Connection_OnDisconnect(self, sender, event_args):
        self._disconnect()
예제 #8
0
class Client(netshared.NetworkEndpoint, metrics.Metrics):
    '''A `Client` manages the connection to a `Server` instance elsewhere.

    Creating an instance of a `Client` and connecting to a server is done
    as shown in the minimalist example below::

        client = Client()
        # Server is running on localhost port 9000
        client.connect(('localhost', 9000))

        # This loop ensures that .update() is called.
        while True:
            client.update()
            # Add a small time delay to prevent pegging the CPU.
            time.sleep(0.0001)

    The `Client` has a number of events that can be hooked into that provide
    notifications of data sent from the server and state changes. An event
    consists of the sender and the argument(in the example below, this
    is the message), eg::

        def my_message_handler(sender, message):
            print("The greeting reads: %s" % message.greeting.value)

        my_client.OnMessage += my_message_handler

    For the `Client.OnMessage` handler example above the argument part of the
    event received is a re-assembled instance of the message that was sent, and
    the greeting field in the message is obtained via
    the fields `value` attribute.

    * `Client.OnConnectRequestAccepted` - Fired when a `Client.connect` request
        has been responded to by the server allowing the connection.
    * `Client.OnConnectRequestRejected` - Fired when a `Client.connect` request
        has been responded to by the server deneying the connection.
    * `Client.OnMessage` - Fired when a message is receieved from the server.
        See above example.
    * `Client.OnError` - An error has occured. The event argument is a string
        detailing the error.
    * `Client.OnDisconnect` - The connection was gracefully closed by the
        Server. If the connection was severed due to a time-out, the
        `Client.OnError` event would fire.
    '''

    _log = logging.getLogger('legume.client')

    def __init__(self, message_factory=messages.message_factory):
        '''
        Create a Client endpoint. A Client is initially in the closed state
        until a call to `connect`.

        A messages factory is required to assemble and disassemble messages for
        pushing down the intertubes to the server endpoint. If a
        message_factory is not explicitly specified then the global
        message_factory will be used.

        :Parameters:
            message_factory : `MessageFactory`
                A message factory.
        '''
        netshared.NetworkEndpoint.__init__(self, message_factory)
        self._address = None
        self._connection = None
        self._disconnecting = False

        self._OnConnectRequestRejected = Event()
        self._OnMessage = Event()
        self._OnConnectRequestAccepted = Event()
        self._OnError = Event()
        self._OnDisconnect = Event()

    # ------------- Properties -------------

    @property
    def connected(self):
        '''
        Returns True if this endpoint's state is `CONNECTED`.
        '''
        return self._state == self.CONNECTED

    @property
    def disconnected(self):
        '''
        Returns True if this endpoint's state is `DISCONNECTED`.
        '''
        return self._state == self.DISCONNECTED

    @property
    def errored(self):
        '''
        Returns True if this endpoint's state is `ERRORED`.
        '''
        return self._state == self.ERRORED

    @property
    def is_server(self):
        '''Returns false.'''
        return False

    @property
    def latency(self):
        if self._connection is not None:
            return self._connection.latency
        else:
            return 0

    @property
    def out_buffer_bytes(self):
        if self._connection is not None:
            return self._connection.out_buffer_bytes
        else:
            return 0

    @property
    def pending_acks(self):
        # TODO: implement
        return 0

    @property
    def in_bytes(self):
        if self._connection is not None:
            return self._connection.in_bytes
        else:
            return 0

    @property
    def out_bytes(self):
        if self._connection is not None:
            return self._connection.out_bytes
        else:
            return 0

    @property
    def in_packets(self):
        # TODO: implement
        return 0

    @property
    def out_packets(self):
        # TODO: implement
        return 0

    @property
    def keepalive_count(self):
        if self._connection is not None:
            return self._connection.keepalive_count
        else:
            return 0

    @property
    def reorder_queue(self):
        if self._connection is not None:
            return self._connection.reorder_queue
        else:
            return 0

    # ------------- Public Methods -------------

    def connect(self, address):
        '''
        Initiate a connection to the server at the specified address.

        This method will put the socket into the `CONNECTING` state. If a
        connection is already established a ClientError exception is raised.

        :Parameters:
            address : (host, port)
                Host address. An ArgumentError exception will be raised for
                an invalid address.
        '''
        if self.is_active():
            raise ClientError(
                'Client cannot reconnect in a CONNECTING or CONNECTED state')
        if len(address) != 2:
            raise ArgumentError('Expected parameter is (host, port) tuple')

        host = address[0]

        try:
            port = int(address[1])
        except ValueError:
            raise ArgumentError('%s is not a valid port' % address[1])
        if port > 65535 or port < 1:
            raise ArgumentError('%s is not a valid port' % port)

        #port = 6385
        self._create_socket()
        self._connect_socket((host, port))
        self._address = (host, port)

        self._connection = Service('Connection', {'parent': self})
        self._connection.OnConnectRequestAccepted += \
            self._Connection_OnConnectRequestAccepted
        self._connection.OnConnectRequestRejected += \
            self._Connection_OnConnectRequestRejected
        self._connection.OnError += self._Connection_OnError
        self._connection.OnDisconnect += self._Connection_OnDisconnect
        self._connection.OnMessage += self._Connection_OnMessage

        request_message = self.message_factory.get_by_name('ConnectRequest')()
        request_message.protocol.value = netshared.PROTOCOL_VERSION
        self._send_reliable_message(request_message)
        self._state = self.CONNECTING

    def disconnect(self):
        '''
        Gracefully disconnect from the host. A disconnection packet is
        sent to the server upon calling the .update() method. The connection
        status of the class instance will not changed to  `DISCONNECTED`
        until .update() is called.
        '''
        if self._connection is not None:
            self._connection.send_message(
                self.message_factory.get_by_name('Disconnected')())
            self._disconnecting = True

    def send_message(self, message):
        '''
        Send a message to the server. The message is added to the output buffer.
        To flush the output buffer call the .update() method. If the client
        is not connected to the server a `ClientError` exception is raised.

        :Parameters:
            message : `BaseMessage`
                The message to be sent
        '''
        if self._state == self.CONNECTED:
            return self._send_message(message)
        else:
            raise ClientError('Cannot send packet - not connected')

    def send_reliable_message(self, message):
        '''
        Send a message to the server with guaranteed delivery. If the
        client is not connected to the server a `ClientError` exception
        is raised.

        :Parameters:
            message : `BaseMessage`
                The message to be sent
        '''
        if self._state == self.CONNECTED:
            return self._send_reliable_message(message)
        else:
            raise ClientError('Cannot send message - not connected')

    def update(self):
        '''
        This method should be called frequently to process incoming data,
        send outgoing data, and raise events.
        '''
        self._log.debug('update')
        if self._state in [self.CONNECTING, self.CONNECTED]:
            self._log.debug('connection.update')
            self._connection.update()

        if self._disconnecting and not self._connection.has_outgoing_packets():
            self._disconnect(raise_event=False)

    # ------------- Private Methods -------------

    def _send_message(self, message):
        return self._connection.send_message(message)

    def _send_reliable_message(self, message):
        return self._connection.send_reliable_message(message)

    def _disconnect(self, raise_event=True):
        self._state = self.DISCONNECTED
        self._shutdown_socket()
        self._disconnecting = False
        if raise_event:
            self.OnDisconnect(self, None)

    # ------------- Events -------------

    def _getOnConnectRequestRejected(self):
        return self._OnConnectRequestRejected

    def _setOnConnectRequestRejected(self, event):
        if isinstance(event, Event):
            self._OnConnectRequestRejected = event
        else:
            raise NEventError('Event must subclass nevent.Event')

    OnConnectRequestRejected = property(_getOnConnectRequestRejected,
                                        _setOnConnectRequestRejected)

    def _getOnMessage(self):
        return self._OnMessage

    def _setOnMessage(self, event):
        if isinstance(event, Event):
            self._OnMessage = event
        else:
            raise NEventError('Event must subclass nevent.Event')

    OnMessage = property(_getOnMessage, _setOnMessage)

    def _getOnConnectRequestAccepted(self):
        return self._OnConnectRequestAccepted

    def _setOnConnectRequestAccepted(self, event):
        if isinstance(event, Event):
            self._OnConnectRequestAccepted = event
        else:
            raise NEventError('Event must subclass nevent.Event')

    OnConnectRequestAccepted = property(_getOnConnectRequestAccepted,
                                        _setOnConnectRequestAccepted)

    def _getOnError(self):
        return self._OnError

    def _setOnError(self, event):
        if isinstance(event, Event):
            self._OnError = event
        else:
            raise NEventError('Event must subclass nevent.Event')

    OnError = property(_getOnError, _setOnError)

    def _getOnDisconnect(self):
        return self._OnDisconnect

    def _setOnDisconnect(self, event):
        if isinstance(event, Event):
            self._OnDisconnect = event
        else:
            raise NEventError('Event must subclass nevent.Event')

    OnDisconnect = property(_getOnDisconnect, _setOnDisconnect)

    # ------------- Connection Event Handlers -------------

    def _Connection_OnConnectRequestRejected(self, sender, event_args):
        self._state = self.ERRORED
        self._shutdown_socket()
        self.OnConnectRequestRejected(self, event_args)

    def _Connection_OnMessage(self, sender, message):
        self.OnMessage(self, message)

    def _Connection_OnConnectRequestAccepted(self, sender, event_args):
        self._state = self.CONNECTED
        self.OnConnectRequestAccepted(self, event_args)

    def _Connection_OnError(self, sender, error_string):
        self._state = self.ERRORED
        self._shutdown_socket()
        self.OnError(self, error_string)

    def _Connection_OnDisconnect(self, sender, event_args):
        self._disconnect()
예제 #9
0
class Peer(metrics.Metrics):
    '''
    A connection to the server. Each connected Client has a paired
    Peer object instance on the server.
    '''
    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

    @property
    def address(self):
        return self._address

    @property
    def connected(self):
        return self._connected

    @property
    def in_bytes(self):
        return self._connection.in_bytes

    @property
    def is_server(self):
        return True

    @property
    def latency(self):
        return self._connection.latency

    @property
    def message_factory(self):
        return self.parent.message_factory

    @property
    def out_buffer_bytes(self):
        return self._connection.out_buffer_bytes

    @property
    def out_bytes(self):
        return self._connection.out_bytes

    @property
    def timeout(self):
        return self.parent.timeout

    @property
    def last_packet_sent_at(self):
        return self._connection.last_packet_sent_at

    def _Connection_OnMessage(self, sender, event_args):
        if not self._connected:
            self._pending_disconnect = True
        else:
            self.OnMessage(self, event_args)

    def _Connection_OnConnectRequest(self, sender, event_args):
        self._connected = self.OnConnectRequest(self, event_args)
        if self._connected is None:
            # No event handler bound, default action is to accept connection.
            self._connected = True
        return self._connected

    def _Connection_OnError(self, sender, error_string):
        self.OnError(self, error_string)

    def _Connection_OnDisconnect(self, sender, event_args):
        self.OnDisconnect(self, sender)

    def do_read(self, callback):
        self.parent.do_read(callback)

    def process_inbound_packet(self, rawData):
        self._connection.process_inbound_packet(rawData)

    def has_packets_to_send(self):
        return self._connection.has_outgoing_packets()

    def send_message(self, packet):
        '''
        Adds a packet to the outgoing buffer to be sent to the client.
        This does not set the in-order or reliable flags.
        packet is an instance of BasePacket.
        Returns the number of bytes added to the output buffer for
        sending this message (header + message bytes)
        '''
        if self._pending_disconnect:
            raise netshared.ServerError(
                'Cannot send_message to a disconnecting peer')
        return self._connection.send_message(packet)

    def send_reliable_message(self, packet):
        if self._pending_disconnect:
            raise netshared.ServerError(
                'Cannot send_reliable_message to a disconnecting peer')
        self._connection.send_reliable_message(packet)

    def disconnect(self):
        '''
        Disconnect this peer. A Disconnected message will be sent to the client
        and this peer object will be deleted once the outgoing buffer for
        this connection has emptied.
        '''
        LOG.info('Sent Disconnected message to client')
        self._connection.send_message(
            self.parent.message_factory.get_by_name('Disconnected')())
        self._pending_disconnect = True

    def update(self):
        self._connection.update()