def _connect(self, seconds_till_next_try: float = 2, timeout: float = -1) -> bool:
        waited = 0
        while not self._exit.is_set() and not self._is_connected:
            # TODO: Get timeout time from Connector.connect
            try:
                self._socket_connection = socket.create_connection(self._address)
                self._socket_connection.settimeout(self._recv_timeout)
                self._is_connected = True
                logger.info(f"Successfully connected to: {str(self._address)}")
                return True
            except ConnectionRefusedError:
                logger.warning("Could not connect to server with address: (%s)", str(self._address))
            except OSError as e:
                logger.error("Is already connected to server")
                logger.debug(e)
                self._is_connected = True
            except socket.gaierror:
                raise ValueError(
                    f"Address error. {self._address} is not a valid address. Address must be of type {SocketAddress}")

            self._exit.wait(seconds_till_next_try)
            waited += seconds_till_next_try
            if waited > timeout >= 0:
                logger.warning("Connection timeout")
                return False
        return False
 def _is_data_packet(self) -> int:
     try:
         function_id = self._function_stack.pop()
         return function_id
     except IndexError:
         logger.error("Trying to pop a empty function stack")
         return -1
Example #3
0
    def run(self) -> None:
        try:
            self._socket_connection.setsockopt(socket.SOL_SOCKET,
                                               socket.SO_REUSEADDR, 1)
            self._socket_connection.bind(self._address)
            logger.info(
                f"Server is now listening on: {self._address[0]}:{self._address[1]}"
            )
            self._socket_connection.listen(4)
        except OSError as e:
            # [WinError 10038] socket closed before
            logger.error(e)
            self._exit.set()
        except socket.gaierror:
            raise ValueError(
                f"Address error. {self._address} is not a valid address. Address must be of type {SocketAddress}"
            )

        while not self._exit.is_set():
            try:
                (connection, addr) = self._socket_connection.accept()
                logger.info("New client connected: (%s)", str(addr))
                client_id = self._produce_next_client_id()
                client_communicator_id = to_server_id(client_id)
                client = self._client_communicator(
                    client_communicator_id, self._address, connection,
                    self._remove_disconnected_client)
                self._add_client(client_communicator_id, client)
            except OSError as e:
                if not isinstance(e, socket.timeout):
                    if not self._exit.is_set():
                        logger.error("TCP connection closed while listening")
Example #4
0
 def get(self, client_id: Optional[int] = None) -> 'ClientCommunicator':
     """Returns the proper ClientCommunicator. The proper one is the one who called the server-side function. This
     function is thread dependent. So if you create a new thread inside the called function you have to store the
     :code:`id` of the current ClientCommunicator and then call this function with this id as optional parameter.
     """
     if client_id is None:
         current_thread: Union[
             ClientCommunicator,
             threading.Thread] = threading.current_thread()
         try:
             client_id = current_thread.id
         except AttributeError:
             """Function may only be called from same thread (thread that called the server function) 
             or with a valid existing client_id!"""
             logger.error(
                 "Captain we have a multithreading problem! Thread dependent function called from another thread"
             )
             raise Exception(
                 "Thread dependent function called from another thread")
     if client_id not in self.clients.keys():
         raise Exception(
             "No client connected. The client_id doesn't match any connected clients."
         )
     assert isinstance(
         self.clients[client_id],
         ClientCommunicator), "Previous checks didnt handle all cases"
     return self.clients[client_id]
 def send_packet(self, packet: Packet) -> bool:
     """Set the proper ids and converts/packs the packet into bytes. Sends the bytes string."""
     IDManager(self._id).set_ids_of_packet(packet)
     send_data = packet.pack()
     successfully_sent = self._send_bytes(send_data)
     if not successfully_sent:
         logger.error("Could not send packet: %s", str(packet))
     return successfully_sent
 def update_ids_by_packet(self, packet: Packet) -> None:
     """Called every time a new packet arrives."""
     self._next_global_id = packet.header.id_container.global_id + 1
     if isinstance(packet, FunctionPacket):
         self._is_function_packet()
     elif isinstance(packet, DataPacket) or isinstance(packet, FileMetaPacket):
         self._is_data_packet()
     else:
         logger.error("Unknown packet_class (%s)", type(packet).__name__)
Example #7
0
 def _remove_disconnected_client(self, communicator: Communicator) -> None:
     """Called when one side stops"""
     try:
         self.clients.pop(communicator.get_id())
     except KeyError:
         import traceback
         traceback.print_stack()
         logger.error(
             f"Trying to remove a client that was already removed! {self.clients}: {communicator.get_id()}"
         )
    def set_ids_of_packet(self, packet: Packet) -> Optional[Packet]:
        """Set ids of packet and adjust internal state"""
        global_id = self._next_global_id
        if isinstance(packet, FunctionPacket):
            func_id = self._is_function_packet()
        elif isinstance(packet, DataPacket) or isinstance(packet, FileMetaPacket):
            func_id = self._is_data_packet()
        else:
            logger.error("Unknown packet_class (%s)", type(packet).__name__)
            return None

        packet.set_ids(func_id, global_id)
        self._next_global_id += 1
        return packet
 def _send_bytes(self, byte_string: bytes) -> bool:
     if not self._is_connected:
         self._connect(timeout=2)
     try:
         encrypted_message = self.cryptographer.encrypt(byte_string)
         if self.cryptographer.is_encrypted_communication:
             send_message = encrypted_message + b"%%"
         else:
             send_message = encrypted_message
         sent = self._socket_connection.sendall(send_message)
         # returns None on success
         return sent is None
     except OSError:
         logger.error(f"Could not send bytes {byte_string}")
         return False
 def _recv_file(self, file_meta_packet: FileMetaPacket, plain_byte_stream: ByteStream,
                encrypted_byte_stream: ByteStream,) -> None:
     """Receives bytes, till the file is fully received. The file is saved at the destination, given in the
     file_meta_packet."""
     file_path = file_meta_packet.dst_path
     existing_bytes = plain_byte_stream.next_all_bytes()
     with open(file_path, "wb+") as file:
         file.write(existing_bytes)
     remaining_bytes = file_meta_packet.file_size - len(existing_bytes)
     appended_data = b""
     with open(file_path, "ab") as file:
         while remaining_bytes > 0:
             data = self._recv_data(plain_byte_stream, encrypted_byte_stream)
             if data is None:
                 logger.error("Connection aborted, while receiving file!")
                 return
             write_data = data[:remaining_bytes]
             appended_data = data[remaining_bytes:]
             file.write(write_data)
             remaining_bytes -= len(write_data)
         plain_byte_stream += appended_data
    def wait_for_response(self):
        """Waits till a data-packet is received and returns it. If a function packet is received instead it is
        executed first."""
        waited = 0.
        while not self._exit.is_set():
            next_global_id = IDManager(self._id).get_next_outer_id()
            try:
                next_packet = self._packets.pop(0)
                actual_outer_id = next_packet.header.id_container.global_id
                if actual_outer_id > next_global_id:
                    logger.error(f"Packet lost! Expected outer_id: {next_global_id}. Got instead: {actual_outer_id}")
                    # TODO: handle
                elif actual_outer_id < next_global_id:
                    logger.error(f"Unhandled Packet! Expected outer_id: {next_global_id}. "
                                 f"actual: {actual_outer_id}, Communicator id: {self._id}")
                    # TODO: handle (if possible)
                else:
                    if isinstance(next_packet, Packet):
                        self._handle_packet(next_packet)
                    if isinstance(next_packet, FunctionPacket):
                        """execute and keep waiting for data"""
                    elif isinstance(next_packet, DataPacket):
                        return next_packet
                    elif isinstance(next_packet, FileMetaPacket):
                        """File is already transmitted."""
                        return DataPacket(**{"return": File.from_meta_packet(next_packet)})
                    else:
                        logger.error(f"Received not implemented Packet class: {type(next_packet)}")

            except IndexError:
                pass  # List is empty -> wait
            self._exit.wait(self._time_till_next_check)
            waited += self._time_till_next_check
            if waited > self.wait_for_response_timeout >= 0:
                logger.warning("wait_for_response waited too long")
                raise TimeoutError("wait_for_response waited too long")