Exemple #1
0
    def accept(self):
        """
        Accepts a connection. The socket must be bound to an address and
        listening for connections.

        Returns:
            Tuple: A pair `(conn, address)` where `conn` is a new socket object
                usable to send and receive data on the connection, and
                `address` is a pair `(host, port)` with the address bound to
                the socket on the other end of the connection.

        Raises:
            XBeeException: If the connection with the XBee device is not open.
            XBeeSocketException: If the socket is not bound or not listening.
        """
        if self.__src_port is None:
            raise XBeeSocketException(message="Socket must be bound")
        if not self.__is_listening:
            raise XBeeSocketException(message="Socket must be listening")

        lock = threading.Condition()
        received_packet = []

        # Define the IPv4 client callback.
        def ipv4_client_callback(packet):
            if (not isinstance(packet, SocketNewIPv4ClientPacket)
                    or packet.socket_id != self.__socket_id):
                return

            # Add the packet to the list and notify the lock.
            received_packet.append(packet)
            lock.acquire()
            lock.notify()
            lock.release()

        # Add the socket IPv4 client callback.
        self.__xbee.add_packet_received_callback(ipv4_client_callback)

        try:
            # Wait until an IPv4 client packet is received.
            lock.acquire()
            lock.wait()
            lock.release()

            conn = socket(self.__xbee, self.__ip_protocol)
            conn.__socket_id = received_packet[0].client_socket_id
            conn.__connected = True

            # Register internal socket state and data reception callbacks.
            conn.__register_state_callback()
            conn.__register_data_received_callback()

            return conn, (received_packet[0].remote_address,
                          received_packet[0].remote_port)
        finally:
            # Always remove the socket IPv4 client callback.
            self.__xbee.del_packet_received_callback(ipv4_client_callback)
Exemple #2
0
    def __send(self, data, send_all=True):
        """
        Sends data to the socket. The socket must be connected to a remote
        socket. Depending on the value of `send_all`, the method will raise an
        exception or return the number of bytes sent when there is an error
        sending a data packet.

        Args:
            data (Bytearray): The data to send.
            send_all (Boolean): `True` to raise an exception when there is an
                error sending a data packet. `False` to return the number of
                bytes sent when there is an error sending a data packet.

        Raises:
            TimeoutException: If the send status response is not received in
                the configured timeout.
            ValueError: If the data to send is `None`.
            ValueError: If the number of bytes to send is `0`.
            XBeeException: If the connection with the XBee device is not open.
            XBeeSocketException: If the socket is not valid.
            XBeeSocketException: If the send status is not `SUCCESS`.
            XBeeSocketException: If the socket is not open.
        """
        if data is None:
            raise ValueError("Data to send cannot be None")
        if len(data) == 0:
            raise ValueError("The number of bytes to send must be at least 1")
        if self.__socket_id is None:
            raise XBeeSocketException(status=SocketStatus.BAD_SOCKET)
        if not self.__xbee.is_open():
            raise XBeeException("XBee device must be open")
        if not self.__connected:
            raise XBeeSocketException(message="Socket is not connected")

        sent_bytes = None if send_all else 0

        # Send as many packets as needed to deliver all the provided data.
        for chunk in self.__split_payload(data):
            send_packet = SocketSendPacket(self.__xbee.get_next_frame_id(),
                                           self.__socket_id, chunk)
            try:
                response_packet = self.__xbee.send_packet_sync_and_get_response(
                    send_packet, timeout=self.__get_timeout())
                self.__check_response(response_packet)
            except (TimeoutException, XBeeSocketException) as exc:
                # Raise the exception only if 'send_all' flag is set, otherwise
                # return the number of bytes sent.
                if send_all:
                    raise exc
                return sent_bytes
            # Increase the number of bytes sent.
            if not send_all:
                sent_bytes += len(chunk)
        # Return the number of bytes sent.
        return sent_bytes
Exemple #3
0
    def __check_response(response_packet):
        """
        Checks the status of the given response packet and throws an
        :class:`.XBeeSocketException` if it is not :attr:`SocketStatus.SUCCESS`.

        Args:
            response_packet (:class:`.XBeeAPIPacket`): The socket response packet.

        Raises:
            XBeeSocketException: If the socket status is not `SUCCESS`.
        """
        if isinstance(response_packet, TXStatusPacket):
            if response_packet.transmit_status != TransmitStatus.SUCCESS:
                raise XBeeSocketException(status=response_packet.transmit_status)
        elif response_packet.status != SocketStatus.SUCCESS:
            raise XBeeSocketException(status=response_packet.status)
Exemple #4
0
    def sendto(self, data, address):
        """
        Sends data to the socket. The socket should not be connected to a
        remote socket, since the destination socket is specified by `address`.

        Args:
            data (Bytearray): The data to send.
            address (Tuple): The address of the destination socket. It must be
                a pair `(host, port)` where `host` is the domain name or string
                representation of an IPv4 and `port` is the numeric port value.

        Returns:
            Integer: The number of bytes sent.

        Raises:
            TimeoutException: If the send status response is not received in
                the configured timeout.
            ValueError: If the data to send is `None`.
            ValueError: If the number of bytes to send is `0`.
            XBeeException: If the connection with the XBee device is not open.
            XBeeSocketException: If the socket is already open.
            XBeeSocketException: If the send status is not `SUCCESS`.
        """
        if data is None:
            raise ValueError("Data to send cannot be None")
        if len(data) == 0:
            raise ValueError("The number of bytes to send must be at least 1")
        if not self.__xbee.is_open():
            raise XBeeException("XBee device must be open")
        if self.__connected:
            raise XBeeSocketException(message="Socket is already connected")

        sent_bytes = 0

        # If the socket is not created, create it first.
        if self.__socket_id is None:
            self.__create_socket()
        # Send as many packets as needed to deliver all the provided data.
        for chunk in self.__split_payload(data):
            send_packet = SocketSendToPacket(self.__xbee.get_next_frame_id(),
                                             self.__socket_id,
                                             IPv4Address(address[0]),
                                             address[1], chunk)
            response_packet = self.__xbee.send_packet_sync_and_get_response(
                send_packet, timeout=self.__get_timeout())
            self.__check_response(response_packet)
            sent_bytes += len(chunk)
        # Return the number of bytes sent.
        return sent_bytes
    def listen(self, backlog=1):
        """
        Enables a server to accept connections.

        Args:
            backlog (Integer, optional): The number of unaccepted connections that the system will allow before refusing
                new connections. If specified, it must be at least 0 (if it is lower, it is set to 0).

        Raises:
            XBeeSocketException: if the socket is not bound.
        """
        if self.__source_port is None:
            raise XBeeSocketException(message="Socket must be bound")

        self.__is_listening = True
        self.__backlog = backlog
    def bind(self, address):
        """
        Binds the socket to the given address. The socket must not already be bound.

        Args:
            address (Tuple): A pair ``(host, port)`` where ``host`` is the local interface (not used) and ``port`` is
                the numeric port value.

        Raises:
            TimeoutException: if the bind response is not received in the configured timeout.
            ValueError: if ``address`` is ``None`` or not a pair ``(host, port)``.
            ValueError: if ``port`` is less than 1 or greater than 65535.
            XBeeException: if the connection with the XBee device is not open.
            XBeeSocketException: if the bind status is not ``SUCCESS``.
            XBeeSocketException: if the socket is already bound.
        """
        # Check address and its contents.
        if address is None or len(address) != 2:
            raise ValueError(
                "Invalid address, it must be a pair (host, port).")

        port = address[1]
        if port < 1 or port > 65535:
            raise ValueError("Port number must be between 1 and 65535.")
        if self.__source_port:
            raise XBeeSocketException(status=SocketStatus.ALREADY_CONNECTED)

        # If the socket is not created, create it first.
        if self.__socket_id is None:
            self.__create_socket()

        # Create, send and check the socket create packet.
        bind_packet = SocketBindListenPacket(
            self.__xbee_device.get_next_frame_id(), self.__socket_id, port)
        response_packet = self.__xbee_device.send_packet_sync_and_get_response(
            bind_packet, timeout=self.__get_timeout())
        self.__check_response(response_packet)

        # Register the internal data 'reception from' callback.
        self.__register_data_received_from_callback()

        # Store the source port.
        self.__source_port = port
    def connect(self, address):
        """
        Connects to a remote socket at the given address.

        Args:
            address (Tuple): A pair ``(host, port)`` where ``host`` is the domain name or string representation of an
                IPv4 and ``port`` is the numeric port value.

        Raises:
            TimeoutException: if the connect response is not received in the configured timeout.
            ValueError: if ``address`` is ``None`` or not a pair ``(host, port)``.
            ValueError: if ``port`` is less than 1 or greater than 65535.
            XBeeException: if the connection with the XBee device is not open.
            XBeeSocketException: if the connect status is not ``SUCCESS``.
        """
        # Check address and its contents.
        if address is None or len(address) != 2:
            raise ValueError(
                "Invalid address, it must be a pair (host, port).")

        host = address[0]
        port = address[1]
        if isinstance(host, IPv4Address):
            host = str(host)
        if port < 1 or port > 65535:
            raise ValueError("Port number must be between 1 and 65535.")

        # If the socket is not created, create it first.
        if self.__socket_id is None:
            self.__create_socket()

        lock = threading.Condition()
        received_state = list()

        # Define the socket state received callback.
        def socket_state_received_callback(socket_id, state):
            # Check the socket ID.
            if socket_id != self.__socket_id:
                return

            # Add the state to the list and notify the lock.
            received_state.append(state)
            lock.acquire()
            lock.notify()
            lock.release()

        # Add the socket state received callback.
        self.__xbee_device.add_socket_state_received_callback(
            socket_state_received_callback)

        try:
            # Create, send and check the socket connect packet.
            connect_packet = SocketConnectPacket(
                self.__xbee_device.get_next_frame_id(), self.__socket_id, port,
                SocketConnectPacket.DEST_ADDRESS_STRING, host)
            response_packet = self.__xbee_device.send_packet_sync_and_get_response(
                connect_packet, timeout=self.__get_timeout())
            self.__check_response(response_packet)

            # Wait until the socket state frame is received confirming the connection.
            if not received_state:
                lock.acquire()
                lock.wait(self.__timeout)
                lock.release()

            # Check if the socket state has been received.
            if not received_state:
                raise TimeoutException(
                    "Timeout waiting for the socket connection")

            # Check if the socket is connected successfully.
            if received_state[0] != SocketState.CONNECTED:
                raise XBeeSocketException(status=received_state[0])

            self.__connected = True

            # Register internal socket state and data reception callbacks.
            self.__register_state_callback()
            self.__register_data_received_callback()
        finally:
            # Always remove the socket state callback.
            self.__xbee_device.del_socket_state_received_callback(
                socket_state_received_callback)