def arg_parser(): """Создаём парсер аргументов коммандной строки и читаем параметры, возвращаем 3 параметра """ parser = argparse.ArgumentParser() parser.add_argument('addr', default=DEFAULT_IP_ADDRESS, nargs='?') parser.add_argument('port', default=DEFAULT_PORT, type=int, nargs='?') parser.add_argument('-m', '--mode', default='listen', nargs='?') # parser.parse_args(sys.argv[1:]) 0 это имя файла, по этому берём все, которые после: namespace = parser.parse_args(sys.argv[1:]) server_address = namespace.addr server_port = namespace.port client_mode = namespace.mode # проверим подходящий номер порта if not 1023 < server_port < 65536: CLIENT_LOGGER.critical( f'Попытка запуска клиента с неподходящим номером порта: {server_port}. ' f'Допустимы адреса с 1024 до 65535. Клиент завершается.') sys.exit(1) # Проверим допустим ли выбранный режим работы клиента if client_mode not in ('listen', 'send'): CLIENT_LOGGER.critical(f'Указан недопустимый режим работы {client_mode}, ' f'допустимые режимы: listen , send') sys.exit(1) return server_address, server_port, client_mode
def message_from_server(server_socket): """Функция - обработчик сообщений других пользователей, поступающих с сервера""" if ACTION in server_socket and server_socket[ACTION] == MESSAGE and \ SENDER in server_socket and MESSAGE_TEXT in server_socket: print(f'Получено сообщение от пользователя ' f'{server_socket[SENDER]}:\n{server_socket[MESSAGE_TEXT]}') CLIENT_LOGGER.info(f'Получено сообщение от пользователя ' f'{server_socket[SENDER]}:\n{server_socket[MESSAGE_TEXT]}') else: CLIENT_LOGGER.error(f'Получено некорректное сообщение с сервера: {server_socket}')
def process_answer(message): """ Функция разбирает ответ сервера :param message: проверяется код ответа от сервера :return: """ CLIENT_LOGGER.debug(f'Разбор сообщения от сервера: {message}') if RESPONSE in message: if message[RESPONSE] == 200: return '200 : OK' return f'400 : {message[ERROR]}' raise ReqFieldMissingError(RESPONSE)
def process_response_answer(message): """ Функция разбирает ответ сервера на сообщение о присутствии, возращает 200 если все ОК или генерирует исключение при ошибке :param message: проверяется код ответа от сервера :return: """ CLIENT_LOGGER.debug(f'Разбор сообщения от сервера: {message}') if RESPONSE in message: if message[RESPONSE] == 200: return '200 : OK' elif message[RESPONSE] == 400: raise ServerError(f'400 : {message[ERROR]}') raise ReqFieldMissingError(RESPONSE)
def create_new_presence(account_name='Guest'): """ Функция генерирует запрос о присутствии клиента :param account_name: Guest - имя аккаунта по умолчанию :return: out: возвращает сгенерированный словарь словарь """ # {'action': 'presence', 'time': 1573760672.167031, 'user': {'account_name': 'Guest'}} out = { ACTION: PRESENCE, TIME: time.time(), USER: { ACCOUNT_NAME: account_name } } CLIENT_LOGGER.debug(f'Сформировано {PRESENCE} сообщение для пользователя {account_name}') return out
def create_message(sock, account_name='Guest'): """Функция запрашивает текст сообщения и возвращает его. Так же завершает работу при вводе подобной комманды """ message = input('Введите сообщение для отправки или \'!!!\' для завершения работы: ') if message == '!!!': sock.close() CLIENT_LOGGER.info('Завершение работы по команде пользователя.') print('Спасибо за использование нашего сервиса!') sys.exit(0) message_dict = { ACTION: MESSAGE, TIME: time.time(), ACCOUNT_NAME: account_name, MESSAGE_TEXT: message } CLIENT_LOGGER.debug(f'Сформирован словарь сообщения: {message_dict}') return message_dict
def arg_parser(): """Создаём парсер аргументов коммандной строки и читаем параметры, возвращаем 3 параметра""" parser = argparse.ArgumentParser() parser.add_argument('addr', default=DEFAULT_IP_ADDRESS, nargs='?') parser.add_argument('port', default=DEFAULT_PORT, type=int, nargs='?') parser.add_argument('-n', '--name', default=None, nargs='?') namespace = parser.parse_args(sys.argv[1:]) server_address = namespace.addr server_port = namespace.port client_name = namespace.name # проверим подходящий номер порта if not 1023 < server_port < 65536: CLIENT_LOGGER.critical( f'Попытка запуска клиента с неподходящим номером порта: {server_port}. ' f'Допустимы адреса с 1024 до 65535. Клиент завершается.') sys.exit(1) return server_address, server_port, client_name
def user_interactive(sock, username): """Функция взаимодействия с пользователем, запрашивает команды, отправляет сообщения""" print_help() while True: command = input('Введите команду: ') if command == 'message': create_message(sock, username) elif command == 'help': print_help() elif command == 'exit': send_message(sock, create_exit_message(username)) print('Завершение соединения.') CLIENT_LOGGER.info('Завершение работы по команде пользователя.') # Задержка неоходима, чтобы успело уйти сообщение о выходе time.sleep(0.5) break else: print( 'Команда не распознана, попробойте снова. help - вывести поддерживаемые команды.' )
def message_from_server(server_socket, my_username): """Функция - обработчик сообщений других пользователей, поступающих с сервера""" while True: try: message = get_message(server_socket) if ACTION in message and message[ACTION] == MESSAGE and \ SENDER in message and DESTINATION in message \ and MESSAGE_TEXT in message and message[DESTINATION] == my_username: print( f'\nПолучено сообщение от пользователя {message[SENDER]}:' f'\n{message[MESSAGE_TEXT]}') CLIENT_LOGGER.info( f'Получено сообщение от пользователя {message[SENDER]}:' f'\n{message[MESSAGE_TEXT]}') else: CLIENT_LOGGER.error( f'Получено некорректное сообщение с сервера: {message}') except IncorrectDataReceivedError: CLIENT_LOGGER.error( f'Не удалось декодировать полученное сообщение.') except (OSError, ConnectionError, ConnectionAbortedError, ConnectionResetError, json.JSONDecodeError): CLIENT_LOGGER.critical(f'Потеряно соединение с сервером.') break
def create_message(sock, account_name='Guest'): """ Функция запрашивает кому отправить сообщение и само сообщение, и отправляет полученные данные на сервер :param sock: :param account_name: :return: """ to_user = input('Введите получателя сообщения: ') message = input('Введите сообщение для отправки: ') message_dict = { ACTION: MESSAGE, SENDER: account_name, DESTINATION: to_user, TIME: time.time(), MESSAGE_TEXT: message } CLIENT_LOGGER.debug(f'Сформирован словарь сообщения: {message_dict}') try: send_message(sock, message_dict) CLIENT_LOGGER.info(f'Отправлено сообщение для пользователя {to_user}') except: CLIENT_LOGGER.critical('Потеряно соединение с сервером.') sys.exit(1)
def main(): """Загружаем параметы коммандной строки""" # client.py 192.168.1.2 8079 parser = create_arg_parser() # parser.parse_args(sys.argv[1:]) 0 это имя файла, по этому берём все, которые после: namespace = parser.parse_args(sys.argv[1:]) server_addr = namespace.addr server_port = namespace.port # проверим подходящий номер порта if not 1023 < server_port < 65536: CLIENT_LOGGER.critical( f'Попытка запуска клиента с неподходящим номером порта: {server_port}. ' f'Допустимы адреса с 1024 до 65535. Клиент завершается.') sys.exit(1) CLIENT_LOGGER.info(f'Запущен клиент с парамертами: адрес сервера: ' f'{server_addr}, порт: {server_port}') # Инициализация сокета и обмен try: client_socket = socket(AF_INET, SOCK_STREAM) client_socket.connect((server_addr, server_port)) message_to_server = create_new_presence() send_message(client_socket, message_to_server) answer = process_answer(get_message(client_socket)) CLIENT_LOGGER.info(f'Принят ответ от сервера {answer}') except json.JSONDecodeError: CLIENT_LOGGER.error('Не удалось декодировать полученную Json строку.') except ReqFieldMissingError as missing_error: CLIENT_LOGGER.error( f'В ответе сервера отсутствует необходимое поле {missing_error.missing_field}' ) except ConnectionRefusedError: CLIENT_LOGGER.critical( f'Не удалось подключиться к серверу {server_addr}:{server_port}, ' f'конечный компьютер отверг запрос на подключение.')
def main(): """Загружаем параметы коммандной строки""" # client.py 192.168.1.2 8079 try: server_addr = sys.argv[1] server_port = int(sys.argv[2]) if server_port < 1024 or server_port > 65535: CLIENT_LOGGER.critical( f'Попытка запуска клиента с неподходящим номером порта: {server_port}.' f' Допустимы адреса с 1024 до 65535. Клиент завершается.') raise ValueError except IndexError: server_addr = DEFAULT_IP_ADDRESS server_port = DEFAULT_PORT except ValueError: # print('В качестве порта может быть указано только число в диапазоне от 1024 до 65535.') # Замена принт логами sys.exit(1) CLIENT_LOGGER.info(f'Запущен клиент с парамертами: ' f'адрес сервера: {server_addr}, порт: {server_port}') # Инициализация сокета и обмен client_socket = socket(AF_INET, SOCK_STREAM) client_socket.connect((server_addr, server_port)) message_to_server = create_new_presence() send_message(client_socket, message_to_server) try: answer = process_answer(get_message(client_socket)) CLIENT_LOGGER.info(f'Принят ответ от сервера {answer}') # print(answer) # Замена принта логами # except (ValueError, json.JSONDecodeError): # print('Не удалось декодировать сообщение сервера.') except json.JSONDecodeError: CLIENT_LOGGER.error('Не удалось декодировать полученную Json строку.') except ReqFieldMissingError as missing_error: CLIENT_LOGGER.error(f'В ответе сервера отсутствует необходимое поле ' f'{missing_error.missing_field}') except ConnectionRefusedError: CLIENT_LOGGER.critical( f'Не удалось подключиться к серверу {server_addr}:{server_port}, ' f'конечный компьютер отверг запрос на подключение.')
def main(): """Сообщаем о запуске""" # Загружаем параметы коммандной строки # client.py 192.168.1.2 8079 ... server_address, server_port, client_name = arg_parser() # Если имя пользователя не было задано, необходимо запросить пользователя. if not client_name: client_name = input('Введите имя пользователя: ') """Сообщаем о запуске""" print( f'Консольный месседжер. Клиентский модуль. Имя пользователя: {client_name}' ) CLIENT_LOGGER.info( f'Запущен клиент с парамертами: адрес сервера: {server_address}, ' f'порт: {server_port}, имя пользователя: {client_name}') # Инициализация сокета и сообщение серверу о нашем появлении try: client_socket = socket(AF_INET, SOCK_STREAM) 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 строку.') sys.exit(1) except ServerError as error: CLIENT_LOGGER.error( f'При установке соединения сервер вернул ошибку: {error.text}') sys.exit(1) except ReqFieldMissingError as missing_error: CLIENT_LOGGER.error( f'В ответе сервера отсутствует необходимое поле {missing_error.missing_field}' ) sys.exit(1) except (ConnectionRefusedError, ConnectionError): CLIENT_LOGGER.critical( f'Не удалось подключиться к серверу {server_address}:{server_port}, ' f'конечный компьютер отверг запрос на подключение.') sys.exit(1) else: # Если соединение с сервером установлено корректно, # запускаем клиенский процесс приёма сообщений receiver = Thread(target=message_from_server, args=(client_socket, client_name)) receiver.daemon = True receiver.start() # затем запускаем отправку сообщений и взаимодействие с пользователем. user_interface = Thread(target=user_interactive, args=(client_socket, client_name)) user_interface.daemon = True user_interface.start() CLIENT_LOGGER.debug('Запущены процессы') # Watchdog основной цикл, если один из потоков завершён, # то значит или потеряно соединение или пользователь # ввёл exit. Поскольку все события обработываются в потоках, # достаточно просто завершить цикл. while True: time.sleep(1) if receiver.is_alive() and user_interface.is_alive(): continue break
def main(): """Загружаем параметы коммандной строки""" # client.py 192.168.1.2 8079 """Загружаем параметы коммандной строки""" server_address, server_port, client_mode = arg_parser() CLIENT_LOGGER.info(f'Запущен клиент с парамертами: адрес сервера: ' f'{server_address}, порт: {server_port}') # Инициализация сокета и сообщение серверу о нашем появлении try: client_socket = socket(AF_INET, SOCK_STREAM) client_socket.connect((server_address, server_port)) send_message(client_socket, create_new_presence()) answer = process_response_answer(get_message(client_socket)) CLIENT_LOGGER.info(f'Установлено соединение с сервером. Ответ сервера: {answer}') print(f'Установлено соединение с сервером.') except json.JSONDecodeError: CLIENT_LOGGER.error('Не удалось декодировать полученную Json строку.') sys.exit(1) except ServerError as error: CLIENT_LOGGER.error(f'При установке соединения сервер вернул ошибку: {error.text}') sys.exit(1) except ReqFieldMissingError as missing_error: CLIENT_LOGGER.error(f'В ответе сервера отсутствует необходимое поле {missing_error.missing_field}') sys.exit(1) except ConnectionRefusedError: CLIENT_LOGGER.critical( f'Не удалось подключиться к серверу {server_address}:{server_port}, ' f'конечный компьютер отверг запрос на подключение.') sys.exit(1) else: # Если соединение с сервером установлено корректно, # начинаем обмен с ним, согласно требуемому режиму. # основной цикл прогрммы: if client_mode == 'send': print('Режим работы - отправка сообщений.') else: print('Режим работы - приём сообщений.') while True: # режим работы - отправка сообщений if client_mode == 'send': try: send_message(client_socket, create_message(client_socket)) except (ConnectionResetError, ConnectionError, ConnectionAbortedError): CLIENT_LOGGER.error(f'Соединение с сервером {server_address} было потеряно.') sys.exit(1) # Режим работы приём: if client_mode == 'listen': try: message_from_server(get_message(client_socket)) except (ConnectionResetError, ConnectionError, ConnectionAbortedError): CLIENT_LOGGER.error(f'Соединение с сервером {server_address} было потеряно.') sys.exit(1)