def __disconnect(session_id: int, address: tuple) -> bytes: """ Closes session :param session_id: id of session to close :param address: address of the client :return: answer for the client """ answer = Datagram(Status.OK, Mode.DISCONNECT, session_id) utils.log('removed session: ' + str(session_id) + ' : ' + str(address)) return answer.get_bytes()
def __operation(self, session_id: int, operation: int, num_a: float, num_b: float) -> bytes: """ Makes requested calculations :param session_id: :param operation: id of rewuested operation :param num_a: number a :param num_b: number b :return: answer for the client """ utils.log('received call for ' + Operation.name_from_code(operation) + ' from session: ' + str(session_id)) answer = Datagram(Status.OK, Mode.OPERATION, session_id, operation, num_a, num_b) answer.result_id = self.next_result_id result: float try: if operation == Operation.POWER: result = num_a**num_b elif operation == Operation.LOG: result = log(num_b) / log(num_a) elif operation == Operation.GEO_MEAN: if num_a * num_b < 0: return self.__error(5, Mode.OPERATION, session_id, operation) result = sqrt(num_a * num_b) elif operation == Operation.BIN_COE: if num_b > num_a or num_a < 0 or num_b < 0: return self.__error(5, Mode.OPERATION, session_id, operation) result = factorial(num_a) / (factorial(num_a - num_b) * factorial(num_b)) except OverflowError: return self.__error(Error.MAX_VALUE_EXCEEDED, Mode.OPERATION, session_id, operation) if result == float('inf'): return self.__error(Error.MAX_VALUE_EXCEEDED, Mode.OPERATION, session_id, operation) self.results_storage[session_id][self.next_result_id] = \ (operation, num_a, num_b, session_id, result, self.next_result_id) self.next_result_id += 1 answer.result = result return answer.get_bytes()
def __is_alive(self, session_id: int, handler: Thread) -> bytes: """ Handles is alive request If session was closed on server, sends information about it to the client :param session_id: id of session to close :param handler: session handler :return: answer for the client """ if self.sessions[handler]: answer = Datagram(Status.OK, Mode.IS_ALIVE, session_id) else: answer = Datagram(Status.REFUSED, Mode.IS_ALIVE, session_id) return answer.get_bytes()
def __connect(self, address: tuple) -> (bytes, int): """ Establish new session :param address: client address :return: (answer to the client , given session_id) """ # get id self.next_id_lock.acquire() given_id = self.next_id self.next_id += 1 self.next_id_lock.release() # prepare answer answer = Datagram(Status.OK, Mode.CONNECT, given_id) utils.log('new session: ' + str(given_id) + ' : ' + str(address[0])) return answer.get_bytes(), given_id
def __error(code: int, mode: int = Mode.ERROR, session_id: int = 0, operation: int = 0) -> bytes: """ Returns error answer :param code: error code :param mode: mode in which error occurred :param session_id: session id in which error occurred :param operation: operation in which error occurred :return: error answer for the client """ utils.log( Error.name_from_code(code) + ' on session: ' + str(session_id) + ' mode: ' + Mode.name_from_code(mode), True) error = Datagram(Status.ERROR, mode, session_id, operation, a=code) return error.get_bytes()
def __send_datagram(self, datagram: Datagram) -> List[Datagram]: """ Sends data to the server :param datagram: data to send :return: list of answers """ self.socket.sendall(datagram.get_bytes()) answer = list() last = False # receive data until last flag is send to true while last is False: # get data answer_bin = self.socket.recv(DATAGRAM_SIZE) try: # decode data answer_data = Datagram.from_bytes(answer_bin) except (bitstring.ReadError, ValueError, TypeError) as e: # if data was unreadable utils.log('error reading datagram: ' + str(e), True) print('error reading datagram') else: # check last flag if answer_data.last: last = True # proceed received errors if answer_data.status == Status.ERROR: print('error on server: ' + Mode.name_from_code(answer_data.mode) + ' - ' + Error.name_from_code(answer_data.a)) elif answer_data.status == Status.REFUSED: print('server refused to ' + Mode.name_from_code(answer_data.mode) + ' reason: ' + Error.name_from_code(answer_data.a)) # add received data to answers answer.append(answer_data) return answer
def __query_by_session_id(self): datagram = Datagram(Status.NEW, Mode.QUERY_BY_SESSION_ID, self.session_id) answer = self.__send_datagram(datagram) if answer[0].status == Status.OK: for result in answer: print('session_id = ' + str(result.session_id) + "\t" + ' result id = ' + str(result.result_id) + "\t" + ' operation: ' + str(Operation.name_from_code(result.operation)) + "\t" + ' a = ' + str(result.a) + "\t" + ' b = ' + str(result.b) + "\t" + ' result = ' + str(result.result))
def __query_by_result_id(self, session_id: int, given_session_id: int, result_id: int) -> bytes: """ Gets one result :param session_id: id of session to look for :param given_session_id: id of session requesting query :param result_id: id of result to look for :return: answer for the client """ utils.log('querying by result id: ' + str(result_id) + 'for ' + str(given_session_id)) if session_id != given_session_id: return self.__error(Error.UNAUTHORISED, Mode.QUERY_BY_SESSION_ID, session_id) if session_id not in self.results_storage: return self.__error(Error.NOT_EXISTING_DATA, Mode.QUERY_BY_SESSION_ID_CMD) session_results = self.results_storage[session_id] if result_id not in session_results: return self.__error(Error.UNAUTHORISED, Mode.QUERY_BY_RESULT_ID, session_id) answer = Datagram( Status.OK, Mode.QUERY_BY_RESULT_ID, session_id, operation=self.results_storage[session_id][result_id][0], a=self.results_storage[session_id][result_id][1], b=self.results_storage[session_id][result_id][2], result=self.results_storage[session_id][result_id][4], result_id=result_id, ) return answer.get_bytes()
def __query_by_result_id(self, result_id: int): datagram = Datagram(Status.NEW, Mode.QUERY_BY_RESULT_ID, self.session_id, result_id=result_id) answer = self.__send_datagram(datagram)[0] if answer.status == Status.OK: # TODO: [Artur] improve presentation of result print('session_id = ' + str(answer.session_id) + "\t" + ' result id = ' + str(answer.result_id) + "\t" + ' operation: ' + str(Operation.name_from_code(answer.operation)) + "\t" + ' a = ' + str(answer.a) + "\t" + ' b = ' + str(answer.b) + "\t" + ' result = ' + str(answer.result))
def __query_by_session_id(self, session_id: int, given_session_id: int, connection: socket) -> bytes: """ Gets all results of session :param session_id: id of session to look for :param given_session_id: id of session requesting query :param connection: connection socket :return: answer for the client """ utils.log('querying by session_id: ' + str(session_id) + ' for ' + str(given_session_id)) if session_id != given_session_id: return self.__error(Error.UNAUTHORISED, Mode.QUERY_BY_SESSION_ID, session_id) if session_id not in self.results_storage: return self.__error(Error.NOT_EXISTING_DATA, Mode.QUERY_BY_SESSION_ID) if not self.results_storage[session_id]: return self.__error(Error.NOT_EXISTING_DATA, Mode.QUERY_BY_SESSION_ID) results = self.results_storage[session_id] answer: List[Datagram] = list() for result_id, result in results.items(): answer.append( Datagram(Status.OK, Mode.QUERY_BY_SESSION_ID, session_id, operation=result[0], a=result[1], b=result[2], result=result[4], result_id=result_id, last=False)) # send all except last results to the client for i in range(0, len(answer) - 1): connection.sendall(answer[i].get_bytes()) # return last result to send answer[len(answer) - 1].last = True return answer[len(answer) - 1].get_bytes()
def __connect(self) -> None: """ Connects to the server """ self.connected_lock.acquire() utils.log('connecting to : ' + self.host + ':' + str(self.port)) # connect to the server self.socket.connect((self.host, self.port)) # get session id datagram = Datagram(Status.NEW, Mode.CONNECT) answer = self.__send_datagram(datagram)[0] self.session_id = answer.session_id if answer.status == Status.OK: utils.log('connected to : ' + self.host + ':' + str(self.port)) self.connected = True else: utils.log(self.host + ':' + str(self.port) + ' refused to connect') self.connected = False self.connected_lock.release()
def __disconnect(self) -> None: """ Disconnects from the server """ self.connected_lock.acquire() utils.log('disconnecting from : ' + self.host + ':' + str(self.port)) # send disconnect request datagram = Datagram(Status.NEW, Mode.DISCONNECT, self.session_id) answer = self.__send_datagram(datagram)[0] if answer.status == Status.OK: utils.log('disconnected from : ' + self.host + ':' + str(self.port)) # close connection self.socket.close() self.connected = False else: utils.log( 'cannot disconnect from : ' + self.host + ':' + str(self.port) + ' error code: ' + str(answer.a), True) self.connected_lock.release()
def __is_alive(self) -> None: """ Checks if server is still available""" while self.connected: self.connected_lock.acquire() datagram = Datagram(Status.NEW, Mode.IS_ALIVE, self.session_id) answer: List[Datagram] try: answer = self.__send_datagram(datagram)[0] except (ConnectionAbortedError, ConnectionResetError): utils.log('server went down') self.connected = False if answer.status != Status.OK: utils.log('server rejected session') self.connected = False if not self.connected: print('press ENTER to exit') self.connected_lock.release() time.sleep(1)
def handle_incoming_connection(self, connection: socket, address: tuple, handler: Thread) -> None: """ Handles session :param connection: socket with established cconnection :param address: address of client :param handler: handler object """ # create variable for storing id session_id = 0 # handle requests while self.sessions[handler]: try: # receive data data = connection.recv(DATAGRAM_SIZE) answer: Datagram = None # noinspection PyBroadException try: # decode data datagram = Datagram.from_bytes(data) # utils.log('received: ' + str(datagram)) answer: bytes if datagram.mode == Mode.CONNECT: answer, session_id = self.__connect(address) self.results_storage[session_id] = {} elif datagram.session_id == session_id: if datagram.mode == Mode.IS_ALIVE: answer = self.__is_alive(datagram.session_id, handler) elif datagram.mode == Mode.DISCONNECT: answer = self.__disconnect(datagram.session_id, address) self.sessions[handler] = False elif datagram.mode == Mode.OPERATION: answer = self.__operation(datagram.session_id, datagram.operation, datagram.a, datagram.b) elif datagram.mode == Mode.QUERY_BY_SESSION_ID: answer = self.__query_by_session_id( session_id, datagram.session_id, connection) elif datagram.mode == Mode.QUERY_BY_RESULT_ID: answer = self.__query_by_result_id( session_id, datagram.session_id, datagram.result_id) else: # if authorization didn't succeed answer = self.__error(Error.UNAUTHORISED) except (bitstring.ReadError, ValueError, TypeError) as e: # if data was unreadable utils.log("datagram exception: " + str(e), True) answer = self.__error(Error.CANNOT_READ_DATAGRAM, Mode.ERROR, session_id) except Exception as e: # if any other exception occurred utils.log("exception: " + str(e), True) answer = self.__error(Error.INTERNAL_SERVER_ERROR, Mode.ERROR, session_id) finally: # send answer to the client connection.sendall(answer) except (ConnectionAbortedError, ConnectionResetError): # if session was closed unsafely utils.log('breaking listening for session: ' + str(session_id)) self.sessions[handler] = False # after closing session safely close connection connection.close() utils.log('session closed: ' + str(session_id))
def __operation(self, operation: int, a: float, b: float): datagram = Datagram(Status.NEW, Mode.OPERATION, self.session_id, operation, a, b) answer = self.__send_datagram(datagram)[0] if answer.status == Status.OK: print(str(answer.result) + '\t:' + str(answer.result_id))