예제 #1
0
 def test_get_message(self):
     """
     Тест функции приёма сообщения
     :return:
     """
     test_sock_ok = TestSocket(self.test_dict_recv_ok)
     test_sock_err = TestSocket(self.test_dict_recv_err)
     # тест корректной расшифровки корректного словаря
     self.assertEqual(get_message(test_sock_ok), self.test_dict_recv_ok)
     # тест корректной расшифровки ошибочного словаря
     self.assertEqual(get_message(test_sock_err), self.test_dict_recv_err)
예제 #2
0
def user_list_request(sock, username):
    CLIENT_LOGGER.debug(f'Запрос списка известных пользователей {username}')
    req = {ACTION: USERS_REQUEST, TIME: time.time(), ACCOUNT_NAME: username}
    send_message(sock, req)
    ans = get_message(sock)
    if RESPONSE in ans and ans[RESPONSE] == 202:
        return ans[LIST_INFO]
    else:
        raise ServerError
예제 #3
0
    def run(self):
        # Инициализация Сокета
        global new_connection
        self.init_socket()

        # Основной цикл программы сервера
        while True:
            # Ждём подключения, если таймаут вышел, ловим исключение.
            try:
                client_socket, client_address = self.sock.accept()
            except OSError:
                pass
            else:
                SERVER_LOGGER.info(f'Установлено соедение с ПК {client_address}')
                self.clients.append(client_socket)

            recv_data_lst = []
            send_data_lst = []
            err_lst = []
            # Проверяем на наличие ждущих клиентов
            try:
                if self.clients:
                    recv_data_lst, send_data_lst, err_lst = select(self.clients, self.clients, [], 0)
            except OSError as err:
                SERVER_LOGGER.error(f'Ошибка работы с сокетами: {err}')

            # принимаем сообщения и если ошибка, исключаем клиента.
            if recv_data_lst:
                for client_with_message in recv_data_lst:
                    try:
                        self.process_client_message(get_message(client_with_message), client_with_message)
                    except OSError:
                        # Ищем клиента в словаре клиентов и удаляем его из него
                        # и  базы подключённых
                        SERVER_LOGGER.info(f'Клиент {client_with_message.getpeername()} '
                                           f'отключился от сервера.')
                        for name in self.names:
                            if self.names[name] == client_with_message:
                                self.database.user_logout(name)
                                del self.names[name]
                                break
                        self.clients.remove(client_with_message)
                        with conflag_lock:
                            new_connection = True

                    # Если есть сообщения, обрабатываем каждое.
                    for message in self.messages:
                        try:
                            self.process_message(message, send_data_lst)
                        except (ConnectionAbortedError, ConnectionError, ConnectionResetError, ConnectionRefusedError):
                            SERVER_LOGGER.info(f'Связь с клиентом с именем {message[DESTINATION]} была потеряна')
                            self.clients.remove(self.names[message[DESTINATION]])
                            self.database.user_logout(message[DESTINATION])
                            del self.names[message[DESTINATION]]
                            with conflag_lock:
                                new_connection = True
                    self.messages.clear()
예제 #4
0
def contacts_list_request(sock, name):
    CLIENT_LOGGER.debug(f'Запрос контакт листа для пользователся {name}')
    req = {ACTION: GET_CONTACTS, TIME: time.time(), USER: name}
    CLIENT_LOGGER.debug(f'Сформирован запрос {req}')
    send_message(sock, req)
    ans = get_message(sock)
    CLIENT_LOGGER.debug(f'Получен ответ {ans}')
    if RESPONSE in ans and ans[RESPONSE] == 202:
        return ans[LIST_INFO]
    else:
        raise ServerError
예제 #5
0
    def run(self):
        """Функция - обработчик сообщений других пользователей, поступающих с сервера"""
        while True:
            # Отдыхаем секунду и снова пробуем захватить сокет.
            # если не сделать тут задержку, то второй поток может достаточно долго ждать освобождения сокета.
            time.sleep(1)
            with sock_lock:
                try:
                    message = get_message(self.sock)

                # Принято некорректное сообщение
                except IncorrectDataReceivedError:
                    CLIENT_LOGGER.error(
                        f'Не удалось декодировать полученное сообщение.')
                # Вышел таймаут соединения если errno = None, иначе обрыв соединения.
                except OSError as err:
                    if err.errno:
                        CLIENT_LOGGER.critical(
                            f'Потеряно соединение с сервером.')
                        break
                # Проблемы с соединением
                except (ConnectionError, ConnectionAbortedError,
                        ConnectionResetError, json.JSONDecodeError):
                    CLIENT_LOGGER.critical(f'Потеряно соединение с сервером.')
                    break
                # Если пакет корретно получен выводим в консоль и записываем в базу.
                else:
                    if ACTION in message and message[ACTION] == MESSAGE and SENDER in message and DESTINATION in message \
                            and MESSAGE_TEXT in message and message[DESTINATION] == self.account_name:
                        print(
                            f'\nПолучено сообщение от пользователя {message[SENDER]}:\n{message[MESSAGE_TEXT]}'
                        )
                        # Захватываем работу с базой данных и сохраняем в неё сообщение
                        with database_lock:
                            try:
                                self.database.save_message(
                                    message[SENDER], self.account_name,
                                    message[MESSAGE_TEXT])
                            except:
                                CLIENT_LOGGER.error(
                                    'Ошибка взаимодействия с базой данных')

                        CLIENT_LOGGER.info(
                            f'Получено сообщение от пользователя {message[SENDER]}:\n{message[MESSAGE_TEXT]}'
                        )
                    else:
                        CLIENT_LOGGER.error(
                            f'Получено некорректное сообщение с сервера: {message}'
                        )
예제 #6
0
def remove_contact(sock, username, contact):
    CLIENT_LOGGER.debug(f'Создание контакта {contact}')
    req = {
        ACTION: REMOVE_CONTACT,
        TIME: time.time(),
        USER: username,
        ACCOUNT_NAME: contact
    }
    send_message(sock, req)
    ans = get_message(sock)
    if RESPONSE in ans and ans[RESPONSE] == 200:
        pass
    else:
        raise ServerError('Ошибка удаления клиента')
    print('Удачное удаление')
예제 #7
0
    def run(self):
        """Метод основной цикл потока."""
        # Инициализация Сокета
        self.init_socket()

        # Основной цикл программы сервера
        while self.running:
            # Ждём подключения, если таймаут вышел, ловим исключение.
            try:
                client, client_address = self.sock.accept()
            except OSError:
                pass
            else:
                logger.info(f'Установлено соедение с ПК {client_address}')
                client.settimeout(5)
                self.clients.append(client)

            recv_data_lst = []
            send_data_lst = []
            err_lst = []
            # Проверяем на наличие ждущих клиентов
            try:
                if self.clients:
                    recv_data_lst, self.listen_sockets, \
                    self.error_sockets = select.select(
                        self.clients, self.clients, [], 0)
            except OSError as err:
                logger.error(f'Ошибка работы с сокетами: {err.errno}')

            # принимаем сообщения и если ошибка, исключаем клиента.
            if recv_data_lst:
                for client_with_message in recv_data_lst:
                    try:
                        self.process_client_message(
                            get_message(client_with_message),
                            client_with_message)
                    except (OSError, json.JSONDecodeError, TypeError) as err:
                        logger.debug(f'Getting data from client exception.',
                                     exc_info=err)
                        self.remove_client(client_with_message)
예제 #8
0
def main():
    """Сообщаем о запуске"""
    print('Консольный месседжер. Клиентский модуль.')
    # Загружаем параметы коммандной строки
    # client.py 192.168.1.2 8079 ...
    server_address, server_port, client_name = arg_parser()

    # Если имя пользователя не было задано, необходимо запросить пользователя.
    if not client_name:
        client_name = input('Введите имя пользователя: ')
    else:
        print(f'Клиентский модуль запущен с именем: {client_name}')

    CLIENT_LOGGER.info(
        f'Запущен клиент с парамертами: адрес сервера: {server_address}, '
        f'порт: {server_port}, имя пользователя: {client_name}')

    # Инициализация сокета и сообщение серверу о нашем появлении
    try:
        client_socket = socket(AF_INET, SOCK_STREAM)

        # Таймаут 1 секунда, необходим для освобождения сокета.
        client_socket.settimeout(1)

        client_socket.connect((server_address, server_port))
        send_message(client_socket, create_new_presence(client_name))
        answer = process_response_answer(get_message(client_socket))
        CLIENT_LOGGER.info(
            f'Установлено соединение с сервером. Ответ сервера: {answer}')
        print(f'Установлено соединение с сервером.')
    except json.JSONDecodeError:
        CLIENT_LOGGER.error('Не удалось декодировать полученную Json строку.')
        exit(1)
    except ServerError as error:
        CLIENT_LOGGER.error(
            f'При установке соединения сервер вернул ошибку: {error.text}')
        exit(1)
    except ReqFieldMissingError as missing_error:
        CLIENT_LOGGER.error(
            f'В ответе сервера отсутствует необходимое поле {missing_error.missing_field}'
        )
        exit(1)
    except (ConnectionRefusedError, ConnectionError):
        CLIENT_LOGGER.critical(
            f'Не удалось подключиться к серверу {server_address}:{server_port}, '
            f'конечный компьютер отверг запрос на подключение.')
        exit(1)
    else:
        # Инициализация БД
        database = ClientDatabase(client_name)
        database_load(client_socket, database, client_name)

        # Если соединение с сервером установлено корректно,
        # запускаем поток взаимодействия с пользователем
        module_sender = ClientSender(client_name, client_socket, database)
        module_sender.daemon = True
        module_sender.start()
        CLIENT_LOGGER.debug('Запущены процессы')

        # затем запускаем поток - приёмник сообщений.
        module_receiver = ClientReader(client_name, client_socket, database)
        module_receiver.daemon = True
        module_receiver.start()

        # Watchdog основной цикл, если один из потоков завершён,
        # то значит или потеряно соединение или пользователь
        # ввёл exit. Поскольку все события обработываются в потоках,
        # достаточно просто завершить цикл.
        while True:
            time.sleep(1)
            if module_receiver.is_alive() and module_sender.is_alive():
                continue
            break
예제 #9
0
 def autorize_user(self, message, sock):
     """Метод реализующий авторизцию пользователей."""
     # Если имя пользователя уже занято то возвращаем 400
     logger.debug(f'Start auth process for {message[USER]}')
     if message[USER][ACCOUNT_NAME] in self.names.keys():
         response = RESPONSE_400
         response[ERROR] = 'Имя пользователя уже занято.'
         try:
             logger.debug(f'Username busy, sending {response}')
             send_message(sock, response)
         except OSError:
             logger.debug('OS Error')
             pass
         self.clients.remove(sock)
         sock.close()
     # Проверяем что пользователь зарегистрирован на сервере.
     elif not self.database.check_user(message[USER][ACCOUNT_NAME]):
         response = RESPONSE_400
         response[ERROR] = 'Пользователь не зарегистрирован.'
         try:
             logger.debug(f'Unknown username, sending {response}')
             send_message(sock, response)
         except OSError:
             pass
         self.clients.remove(sock)
         sock.close()
     else:
         logger.debug('Correct username, starting passwd check.')
         # Иначе отвечаем 511 и проводим процедуру авторизации
         # Словарь - заготовка
         message_auth = RESPONSE_511
         # Набор байтов в hex представлении
         random_str = binascii.hexlify(os.urandom(64))
         # В словарь байты нельзя, декодируем (json.dumps -> TypeError)
         message_auth[DATA] = random_str.decode('ascii')
         # Создаём хэш пароля и связки с рандомной строкой, сохраняем
         # серверную версию ключа
         hash = hmac.new(
             self.database.get_hash(message[USER][ACCOUNT_NAME]),
             random_str, 'MD5')
         digest = hash.digest()
         logger.debug(f'Auth message = {message_auth}')
         try:
             # Обмен с клиентом
             send_message(sock, message_auth)
             ans = get_message(sock)
         except OSError as err:
             logger.debug('Error in auth, data:', exc_info=err)
             sock.close()
             return
         client_digest = binascii.a2b_base64(ans[DATA])
         # Если ответ клиента корректный, то сохраняем его в список
         # пользователей.
         if RESPONSE in ans and ans[RESPONSE] == 511 \
                 and hmac.compare_digest(digest, client_digest):
             self.names[message[USER][ACCOUNT_NAME]] = sock
             client_ip, client_port = sock.getpeername()
             try:
                 send_message(sock, RESPONSE_200)
             except OSError:
                 self.remove_client(message[USER][ACCOUNT_NAME])
             # добавляем пользователя в список активных
             # и если у него изменился открытый ключ, сохраняем новый
             self.database.user_login(message[USER][ACCOUNT_NAME],
                                      client_ip, client_port,
                                      message[USER][PUBLIC_KEY])
         else:
             response = RESPONSE_400
             response[ERROR] = 'Неверный пароль.'
             try:
                 send_message(sock, response)
             except OSError:
                 pass
             self.clients.remove(sock)
             sock.close()