def run(self): """ Method containing the main cycle of the transport stream :return: """ client_log.debug('Запущен процесс - приёмник собщений с сервера.') while self.running: # Отдыхаем секунду и снова пробуем захватить сокет. # если не сделать тут задержку, то отправка может достаточно долго # ждать освобождения сокета. time.sleep(1) message = None with socket_lock: try: self.transport.settimeout(0.5) message = get_message(self.transport) except OSError as err: if err.errno: client_log.critical(f'Потеряно соединение с сервером.') self.running = False self.connection_lost.emit() # Проблемы с соединением except (ConnectionError, ConnectionAbortedError, ConnectionResetError, json.JSONDecodeError, TypeError): client_log.debug(f'Потеряно соединение с сервером.') self.running = False self.connection_lost.emit() finally: self.transport.settimeout(5) # Если сообщение получено, то вызываем функцию обработчик: if message: client_log.debug(f'Принято сообщение с сервера: {message}') self.process_server_ans(message)
def remove_contact(self, contact): """ Method that sends information about deleting a contact to the server :param contact: :return: """ client_log.debug(f'Удаление контакта {contact}') req = { ACTION: REMOVE_CONTACT, TIME: time.time(), USER: self.username, ACCOUNT_NAME: contact } with socket_lock: send_message(self.transport, req) self.process_server_ans(get_message(self.transport))
def add_contact(self, contact): """ A method that sends information about adding a contact to the server. :param contact: :return: """ client_log.debug(f'Создание контакта {contact}') req = { ACTION: ADD_CONTACT, TIME: time.time(), USER: self.username, ACCOUNT_NAME: contact } with socket_lock: send_message(self.transport, req) self.process_server_ans(get_message(self.transport))
def contacts_list_update(self): """ Method updating the list of contacts from the server. :return: """ self.database.contacts_clear() client_log.debug(f'Запрос контакт листа для пользователся {self.name}') req = {ACTION: GET_CONTACTS, TIME: time.time(), USER: self.username} client_log.debug(f'Сформирован запрос {req}') with socket_lock: send_message(self.transport, req) ans = get_message(self.transport) client_log.debug(f'Получен ответ {ans}') if RESPONSE in ans and ans[RESPONSE] == 202: for contact in ans[LIST_INFO]: self.database.add_contact(contact) else: client_log.error('Не удалось обновить список контактов.')
def key_request(self, user): """ Method that requests the public key of the user from the server :param user: :return: """ client_log.debug(f'Запрос публичного ключа для {user}') req = { ACTION: PUBLIC_KEY_REQUEST, TIME: time.time(), ACCOUNT_NAME: user } with socket_lock: send_message(self.transport, req) ans = get_message(self.transport) if RESPONSE in ans and ans[RESPONSE] == 511: return ans[DATA] else: client_log.error(f'Не удалось получить ключ собеседника{user}.')
def send_message(self, to, message): """ Method sending messages to the server for the user :param to: :param message: :return: """ message_dict = { ACTION: MESSAGE, SENDER: self.username, DESTINATION: to, TIME: time.time(), MESSAGE_TEXT: message } client_log.debug(f'Сформирован словарь сообщения: {message_dict}') # Необходимо дождаться освобождения сокета для отправки сообщения with socket_lock: send_message(self.transport, message_dict) self.process_server_ans(get_message(self.transport)) client_log.info(f'Отправлено сообщение для пользователя {to}')
def user_list_update(self): """ Method that updates the list of users from the server. :return: """ client_log.debug( f'Запрос списка известных пользователей {self.username}') req = { ACTION: USERS_REQUEST, TIME: time.time(), ACCOUNT_NAME: self.username } with socket_lock: send_message(self.transport, req) ans = get_message(self.transport) if RESPONSE in ans and ans[RESPONSE] == 202: self.database.add_users(ans[LIST_INFO]) else: client_log.error( 'Не удалось обновить список известных пользователей.')
def connection_init(self, port, ip): """ Method responsible for establishing a connection to the server :param port: :param ip: :return: """ # Инициализация сокета и сообщение серверу о нашем появлении self.transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Таймаут необходим для освобождения сокета. self.transport.settimeout(5) # Соединяемся, 5 попыток соединения, флаг успеха ставим в True если # удалось connected = False for i in range(5): client_log.info(f'Попытка подключения №{i + 1}') try: self.transport.connect((ip, port)) except (OSError, ConnectionRefusedError): pass else: connected = True client_log.debug("Connection established.") break time.sleep(1) # Если соединится не удалось - исключение if not connected: client_log.critical('Не удалось установить соединение с сервером') raise ServerError('Не удалось установить соединение с сервером') client_log.debug('Starting auth dialog.') # Запускаем процедуру авторизации # Получаем хэш пароля passwd_bytes = self.password.encode('utf-8') salt = self.username.lower().encode('utf-8') passwd_hash = hashlib.pbkdf2_hmac('sha512', passwd_bytes, salt, 10000) passwd_hash_string = binascii.hexlify(passwd_hash) client_log.debug(f'Passwd hash ready: {passwd_hash_string}') # Получаем публичный ключ и декодируем его из байтов pubkey = self.keys.publickey().export_key().decode('ascii') # Авторизируемся на сервере with socket_lock: presense = { ACTION: PRESENCE, TIME: time.time(), USER: { ACCOUNT_NAME: self.username, PUBLIC_KEY: pubkey } } client_log.debug(f"Presense message = {presense}") # Отправляем серверу приветственное сообщение. try: send_message(self.transport, presense) ans = get_message(self.transport) client_log.debug(f'Server response = {ans}.') # Если сервер вернул ошибку, бросаем исключение. if RESPONSE in ans: if ans[RESPONSE] == 400: raise ServerError(ans[ERROR]) elif ans[RESPONSE] == 511: # Если всё нормально, то продолжаем процедуру # авторизации. ans_data = ans[DATA] hash = hmac.new(passwd_hash_string, ans_data.encode('utf-8'), 'MD5') digest = hash.digest() my_ans = RESPONSE_511 my_ans[DATA] = binascii.b2a_base64(digest).decode( 'ascii') send_message(self.transport, my_ans) self.process_server_ans(get_message(self.transport)) except (OSError, json.JSONDecodeError) as err: client_log.debug(f'Connection error.', exc_info=err) raise ServerError('Сбой соединения в процессе авторизации.')
def online_users_list_request(self): req = {ACTION: ONLINE_USERS_REQUEST, TIME: time.time()} with socket_lock: send_message(self.transport, req) self.process_server_ans(get_message(self.transport))