def _handle_stream(self, connection, address): """A routine to handle incoming requests from a TCP client. """ # Receive an introductory message with the message type. packed_message_type = recvall(connection, 4) message_type_int = unpack_int(packed_message_type)[0] packed_message_length = recvall(connection, 4) message_length = unpack_int(packed_message_length)[0] try: message_type = EvaluatorMessageTypes(message_type_int) except ValueError as e: trace = traceback.format_exception(None, e, e.__traceback__) logger.info(f"Bad message type received: {trace}") # Discard the unrecognised message. if message_length > 0: recvall(connection, message_length) return if message_type is EvaluatorMessageTypes.Submission: self._handle_job_submission(connection, address, message_length) elif message_type is EvaluatorMessageTypes.Query: self._handle_job_query(connection, message_length)
def _send_query_to_server(self, request_id): """Attempts to connect to the calculation server, and submit the requested calculations. Parameters ---------- request_id: str The id of the job to query. Returns ------- str, optional: The status of the submitted job. Returns None if the calculation has not yet completed. """ server_response = None # Attempt to establish a connection to the server. connection_settings = ( self._connection_options.server_address, self._connection_options.server_port, ) connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connection.connect(connection_settings) try: # Encode the request id into the message. message_type = pack_int(EvaluatorMessageTypes.Query) encoded_request_id = request_id.encode() length = pack_int(len(encoded_request_id)) connection.sendall(message_type + length + encoded_request_id) # Wait for the server response. header = recvall(connection, 4) length = unpack_int(header)[0] # Decode the response from the server. If everything # went well, this should be the finished calculation. if length > 0: encoded_json = recvall(connection, length) server_response = encoded_json.decode() finally: if connection is not None: connection.close() response = None error = None if server_response is not None: response, error = json.loads(server_response, cls=TypedJSONDecoder) return response, error
def test_message_packing(): """Test that packing / unpacking ints works as expected""" assert tcp.unpack_int(tcp.pack_int(20))[0] == 20
def _send_calculations_to_server(self, submission): """Attempts to connect to the calculation server, and submit the requested calculations. Parameters ---------- submission: _Submission The jobs to submit. Returns ------- str, optional: The id which the server has assigned the submitted calculations. This can be used to query the server for when the calculation has completed. Returns None if the calculation could not be submitted. EvaluatorException, optional Any exceptions raised while attempting the submit the request. """ # Attempt to establish a connection to the server. connection_settings = ( self._connection_options.server_address, self._connection_options.server_port, ) connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connection.connect(connection_settings) request_id = None try: # Encode the submission json into an encoded # packet ready to submit to the server. message_type = pack_int(EvaluatorMessageTypes.Submission) encoded_json = submission.json().encode() length = pack_int(len(encoded_json)) connection.sendall(message_type + length + encoded_json) # Wait for confirmation that the server has received # the jobs. header = recvall(connection, 4) length = unpack_int(header)[0] # Decode the response from the server. If everything # went well, this should be the id of the submitted # calculations. encoded_json = recvall(connection, length) request_id, error = json.loads(encoded_json.decode(), cls=TypedJSONDecoder) except Exception as e: trace = traceback.format_exception(None, e, e.__traceback__) error = EvaluatorException(message=trace) finally: if connection is not None: connection.close() # Return the ids of the submitted jobs. return request_id, error