예제 #1
0
class NetClient:
    """
    Main networking class.
    """
    def __init__(self, server_address):
        # server information
        self.server_address = server_address
        self.server_port = 15000
        self.timeout = 4000
        self.server_connection = None

        # networking modules from panda3d
        self.manager = QueuedConnectionManager()
        self.reader = QueuedConnectionReader(self.manager, 0)
        self.writer = ConnectionWriter(self.manager, 0)

        # modules that deal with messages
        self.sender_manager = MessageSendersManager(self)
        self.handler = Handler(self)
        self.section_state_fetcher = SectionStateFetcher(self)

    def connect(self):
        """
        Establish connection with the server.
        """
        self.server_connection = self.manager.open_TCP_client_connection(
            self.server_address, self.server_port, self.timeout)
        if self.server_connection:
            self.reader.add_connection(self.server_connection)
            return True
        return False

    def send_ready_for_updates(self):
        """
        Send message to the server saying that client is ready for updates about game state.
        """
        datagram = PyDatagram()
        ReadyForSyncRequest.build().dump(datagram)
        self.writer.send(datagram, self.server_connection)

    def begin_sync_with_server(self):
        """
        Start synchronizing client's state with server's state.
        """
        core.instance.task_mgr.add(self.listen_for_updates,
                                   "listen-for-updates")

    def listen_for_updates(self, task):
        """
        Listen for any incoming packets from the server.
        """
        if self.reader.data_available():
            datagram = NetDatagram()
            if self.reader.get_data(datagram):
                self.handler.handle_data(datagram)
        return Task.cont
예제 #2
0
class NetworkListener(NetworkManager):
    notify = directNotify.newCategory('NetworkListener')

    def __init__(self, address, port, handler, backlog=10000):
        NetworkManager.__init__(self)

        self.__address = address
        self.__port = port
        self.__handler = handler
        self.__backlog = backlog

        self.__manager = QueuedConnectionManager()
        self.__listener = QueuedConnectionListener(self.__manager, 0)
        self.__reader = QueuedConnectionReader(self.__manager, 0)
        self.__writer = ConnectionWriter(self.__manager, 0)

        self.__socket = None
        self.__handlers = {}

        self.__listen_task = None
        self.__read_task = None
        self.__disconnect_task = None

    def setup(self):
        if not self.__socket:
            self.__socket = self.__manager.open_TCP_server_rendezvous(
                self.__address, self.__port, self.__backlog)

            if not self.__socket:
                raise NetworkError(
                    'Failed to bind TCP socket on address: <%s:%d>!' %
                    (self.__address, self.__port))

            self.__listener.add_connection(self.__socket)

        self.__listen_task = task_mgr.add(
            self.__listen_incoming,
            self.get_unique_name('listen-incoming'),
            taskChain=task_chain)

        self.__read_task = task_mgr.add(self.__read_incoming,
                                        self.get_unique_name('read-incoming'),
                                        taskChain=task_chain)

        self.__disconnect_task = task_mgr.add(
            self.__listen_disconnect,
            self.get_unique_name('listen-disconnect'),
            taskChain=task_chain)

    def __listen_incoming(self, task):
        """
        Polls for incoming connections
        """

        if self.__listener.new_connection_available():
            rendezvous = PointerToConnection()
            address = NetAddress()
            connection = PointerToConnection()

            if self.__listener.get_new_connection(rendezvous, address,
                                                  connection):
                self.__handle_connection(rendezvous, address, connection.p())

        return task.cont

    def __read_incoming(self, task):
        """
        Polls for incoming data
        """

        if self.__reader.data_available():
            datagram = NetworkDatagram()

            if self.__reader.get_data(datagram):
                self.__handle_data(datagram, datagram.get_connection())

        return task.cont

    def __listen_disconnect(self, task):
        """
        Watches all connected socket objects and determines if the stream has ended...
        """

        for handler in self.__handlers.values():
            if not self.__reader.is_connection_ok(handler.connection):
                handler.handle_disconnected()

        return task.cont

    def __has_handler(self, connection):
        """
        Returns True if the handler is queued else False
        """

        return connection in self.__handlers

    def __add_handler(self, handler):
        """
        Adds a handler to the handlers dictionary
        """

        if self.__has_handler(handler.connection):
            return

        handler.setup()
        self.__handlers[handler.connection] = handler
        self.__reader.add_connection(handler.connection)

    def __remove_handler(self, handler):
        """
        Removes a handler from the handlers dictionary
        """

        if not self.__has_handler(handler.connection):
            return

        handler.shutdown()
        self.__reader.remove_connection(handler.connection)
        del self.__handlers[handler.connection]

    def __handle_connection(self, rendezvous, address, connection):
        """
        Handles an incoming connection from the connection listener
        """

        handler = self.__handler(self, rendezvous, address, connection)
        self.__add_handler(handler)

    def __handle_data(self, datagram, connection):
        """
        Handles new data incoming from the connection reader
        """

        if not self.__has_handler(connection):
            return

        self.__handlers[connection].queue(datagram)

    def get_handler_from_channel(self, channel):
        """
        Returns a handler instance if one is associated with that channel
        """

        for connection, handler in self.__handlers.items():

            if handler.channel == channel:
                return handler

        return None

    def handle_send_datagram(self, datagram, connection):
        """
        Sends a datagram to a specific connection
        """

        if not self.__has_handler(connection):
            return

        self.__writer.send(datagram, connection)

    def handle_disconnect(self, handler):
        """
        Disconnects the handlers client socket instance
        """

        self.__manager.close_connection(handler.connection)

    def handle_disconnected(self, handler):
        """
        Handles disconnection of a client socket instance
        """

        self.__remove_handler(handler)

    def shutdown(self):
        if self.__listen_task:
            task_mgr.remove(self.__listen_task)

        if self.__read_task:
            task_mgr.remove(self.__read_task)

        if self.__disconnect_task:
            task_mgr.remove(self.__disconnect_task)

        self.__listen_task = None
        self.__read_task = None
        self.__disconnect_task = None

        self.__listener.remove_connection(self.__socket)
예제 #3
0
class NetworkConnector(NetworkManager):
    notify = directNotify.newCategory('NetworkConnector')

    def __init__(self, dc_loader, address, port, channel, timeout=5000):
        NetworkManager.__init__(self)

        self._dc_loader = dc_loader
        self.__address = address
        self.__port = port
        self._channel = channel
        self.__timeout = timeout

        self.__manager = QueuedConnectionManager()
        self.__reader = QueuedConnectionReader(self.__manager, 0)
        self.__writer = ConnectionWriter(self.__manager, 0)

        self.__socket = None

        self.__read_task = None
        self.__disconnect_task = None

    @property
    def dc_loader(self):
        return self._dc_loader

    @property
    def channel(self):
        return self._channel

    @channel.setter
    def channel(self, channel):
        self._channel = channel

    def setup(self):
        if not self.__socket:
            self.__socket = self.__manager.open_TCP_client_connection(
                self.__address, self.__port, self.__timeout)

            if not self.__socket:
                raise NetworkError(
                    'Failed to connect TCP socket on address: <%s:%d>!' %
                    (self.__address, self.__port))

            self.__reader.add_connection(self.__socket)
            self.register_for_channel(self._channel)

        self.__read_task = task_mgr.add(self.__read_incoming,
                                        self.get_unique_name('read-incoming'),
                                        taskChain=task_chain)

        self.__disconnect_task = task_mgr.add(
            self.__listen_disconnect,
            self.get_unique_name('listen-disconnect'),
            taskChain=task_chain)

    def register_for_channel(self, channel):
        """
        Registers our connections channel with the MessageDirector
        """

        datagram = NetworkDatagram()
        datagram.add_control_header(channel, types.CONTROL_SET_CHANNEL)
        self.handle_send_connection_datagram(datagram)

    def unregister_for_channel(self, channel):
        """
        Unregisters our connections channel from the MessageDirector
        """

        datagram = NetworkDatagram()
        datagram.add_control_header(channel, types.CONTROL_REMOVE_CHANNEL)
        self.handle_send_connection_datagram(datagram)

    def __read_incoming(self, task):
        """
        Polls for incoming data
        """

        if self.__reader.data_available():
            datagram = NetworkDatagram()

            if self.__reader.get_data(datagram):
                self.__handle_data(datagram)

        return task.cont

    def __listen_disconnect(self, task):
        """
        Watches our connected socket object and determines if the stream has ended..
        """

        if not self.__reader.is_connection_ok(self.__socket):
            self.handle_disconnected()
            return task.done

        return task.cont

    def __handle_data(self, datagram):
        """
        Handles incoming data from the connector
        """

        if not datagram.get_length():
            return

        di = NetworkDatagramIterator(datagram)
        code = di.get_uint8()

        self.handle_datagram(di.get_uint64(), di.get_uint64(), di.get_uint16(),
                             di)

    def handle_send_connection_datagram(self, datagram):
        """
        Sends a datagram to our connection
        """

        self.__writer.send(datagram, self.__socket)

    def handle_datagram(self, channel, sender, message_type, di):
        """
        Handles a datagram that was pulled from the queue
        """

    def handle_disconnect(self):
        """
        Disconnects our client socket instance
        """

        self.__manager.close_connection(self.__socket)

    def handle_disconnected(self):
        """
        Handles disconnection when the socket connection closes
        """

        self.unregister_for_channel(self._channel)
        self.__reader.remove_connection(self.__socket)

    def shutdown(self):
        if self.__read_task:
            task_mgr.remove(self.__read_task)

        if self.__disconnect_task:
            task_mgr.remove(self.__disconnect_task)

        self.__read_task = None
        self.__disconnect_task = None
예제 #4
0
class Server(EventUser):
    def __init__(self):
        super().__init__()

        # Support objects
        self.manager = QueuedConnectionManager()
        self.listener = QueuedConnectionListener(self.manager, 0)
        self.reader = QueuedConnectionReader(self.manager, 0)
        self.writer = ConnectionWriter(self.manager, 0)
        self.handler = Handler(self)

        # Server model
        self.session_manager = SessionManager()
        self.notifier_manager = NotifierManager(self)
        self.task_manager = TaskManager(self)
        self.connections = []

        # Socket
        self.tcp_socket = self.manager.open_TCP_server_rendezvous(15000, 1000)
        self.listener.add_connection(self.tcp_socket)

        self.accept_event(Event.CLIENT_DISCONNECTION_PUBLISHED, self.close_connection)

    def run(self):
        """
        Creates threads with active targets for accepting new connections and
        receiving data from existing ones
        """
        Thread(target=self.listen_for_new_connections, daemon=True).start()
        Thread(target=self.listen_for_new_data, daemon=True).start()

    def listen_for_new_connections(self):
        """
        Listens to new connections, when avalibe, creates new session
        and event notifier
        """
        while True:
            if self.listener.new_connection_available():
                rendezvous = PointerToConnection()
                net_address = NetAddress()
                new_connection = PointerToConnection()
                if self.listener.get_new_connection(
                    rendezvous, net_address, new_connection
                ):
                    new_connection = new_connection.p()

                    session = self.session_manager.new_session(
                        new_connection,
                    )

                    self.connections.append(new_connection)
                    self.notifier_manager.new_notifier(session, new_connection)
                    self.task_manager.new_session_task_manager(session, new_connection)
                    self.reader.add_connection(new_connection)

    def listen_for_new_data(self):
        """
        Listens for new data from active connections
        """
        while True:
            if self.reader.data_available():
                datagram = NetDatagram()
                if self.reader.get_data(datagram):
                    connection = datagram.getConnection()
                    session = self.session_manager.for_connection(connection)
                    self.handler.handle_data(
                        datagram,
                        connection,
                        session,
                    )
            time.sleep(0.01)

    def close_connection(self, connection):
        self.manager.close_connection(connection)
        self.connections.remove(connection)

    def find_connection_by_hash(self, connection_hash):
        for connection in self.connections:
            if hash(connection) == connection_hash:
                return connection