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)
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
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)
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)