def message_writer(to_socket, account_name='Anonymous'): """ function requests message text and returns him, also it may leave program, by necessity :param to_socket: :param account_name: :return formed_dict_to_send: """ to_receiver = input('Send message to user: '******'Enter text message: ') message_dict = { ACTION: MESSAGE, SENDER: account_name, DESTINATION: to_receiver, TIME: time.time(), MESSAGE_TEXT: message } CLIENT_SIDE_LOGGER.debug(f'message dict formed: {message_dict}') try: send_message(to_socket, message_dict) CLIENT_SIDE_LOGGER.info(f'message to {to_receiver} was sent') except: CLIENT_SIDE_LOGGER.critical('something going wrong, ' 'connection with server will be lost') sys.exit(1)
def client_initializer(): """ Loading param of cmd and do init of socket with transfer by result """ try: server_addr = sys.argv[1] server_port = int(sys.argv[2]) if server_port < 1024 or server_port > 65535: raise ValueError except IndexError: server_addr = DEFAULT_IP_ADDRESS server_port = DEFAULT_PORT except ValueError: print( 'Only a number between 1024 and 65535 can be specified as a port.') sys.exit(1) transfer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transfer.connect((server_addr, server_port)) confirmation_for_server = launch_presence() send_message(transfer, confirmation_for_server) try: answer = server_response(get_message(transfer)) print(answer) except (ValueError, json.JSONDecodeError): print('Failed to decode server message.')
def client_initializer(): """ Loading param of cmd and do init of socket with transfer by result """ server_addr, server_port, client_mode = argument_parser() CLIENT_SIDE_LOGGER.info( f'client application was launched with parameters: ' f'server address: {server_addr} ' f'port: {server_port}, mode: {client_mode}') try: transfer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transfer.connect((server_addr, server_port)) send_message(transfer, launch_presence()) answer = server_response(get_message(transfer)) CLIENT_SIDE_LOGGER.info(f'received response from server {answer}') print('Сonnection to the server has been established') except json.JSONDecodeError: CLIENT_SIDE_LOGGER.error(f'failed to decode received Json string') sys.exit(1) except ExceptionServerError as error: CLIENT_SIDE_LOGGER.error( f'during attempt to connect, server return error: {error.text}') sys.exit(1) except RequestedFieldAbsentError as missing_error: CLIENT_SIDE_LOGGER.error(f'in server response has no requested field' f'{missing_error.missing_field}') sys.exit(1) except ConnectionRefusedError: CLIENT_SIDE_LOGGER.critical( f'connection to server is unavailable {server_addr}:{server_port}, ' f'request to connect was refused') sys.exit(1) else: if client_mode == 'send': print('Status - preparing to send messages') else: print('Status - receiving messages') while True: if client_mode == 'send': try: send_message(transfer, message_writer(transfer)) except (ConnectionResetError, ConnectionError, ConnectionAbortedError): CLIENT_SIDE_LOGGER.error( f'connection with server {server_addr} was lost') sys.exit(1) if client_mode == 'listen': try: server_msg_handler(get_message(transfer)) except (ConnectionResetError, ConnectionError, ConnectionAbortedError): CLIENT_SIDE_LOGGER.error( f'connection with server {server_addr} was lost') sys.exit(1)
def client_initializer(): """ Loading param of cmd and do init of socket with transfer by result """ print('Welcome to MikeT messenger!') server_addr, server_port, client_name = argument_parser() if not client_name: client_name = input('Enter your name: ') CLIENT_SIDE_LOGGER.info( f'client application was launched with parameters: ' f'server address: {server_addr} ' f'port: {server_port}, mode: {client_name}') try: transfer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transfer.connect((server_addr, server_port)) send_message(transfer, launch_presence(client_name)) answer = server_response(get_message(transfer)) CLIENT_SIDE_LOGGER.info(f'received response from server {answer}') print('Сonnection to the server has been established') except json.JSONDecodeError: CLIENT_SIDE_LOGGER.error(f'failed to decode received Json string') sys.exit(1) except ExceptionServerError as error: CLIENT_SIDE_LOGGER.error( f'during attempt to connect, server return error: {error.text}') sys.exit(1) except RequestedFieldAbsentError as missing_error: CLIENT_SIDE_LOGGER.error(f'in server response has no requested field' f'{missing_error.missing_field}') sys.exit(1) except (ConnectionRefusedError, ConnectionError): CLIENT_SIDE_LOGGER.critical(f'connection to server is not available' f' {server_addr}:{server_port}, ' f'request to connect was refused') sys.exit(1) else: target_side = threading.Thread(target=server_msg_handler, args=(transfer, client_name)) target_side.daemon = True target_side.start() user_side = threading.Thread(target=user_online, args=(transfer, client_name)) user_side.daemon = True user_side.start() CLIENT_SIDE_LOGGER.debug('process started') while True: time.sleep(1) if target_side.is_alive() and user_side.is_alive(): continue break
def test_send_message(self): """ testing correct work of delivery fucntion creating test socket and checking correct of dictionary sends """ test_socket = TestSocket(self.test_dict_send) send_message(test_socket, self.test_dict_send) self.assertEqual(test_socket.encoded_message, test_socket.received_message) with self.assertRaises(Exception): send_message(test_socket, test_socket)
def server_launcher(): """ loading params of cmd, if they're not set, will defined by default, after that function detects addr which will be listen, prepares port and starts to receive a information """ try: if '-p' in sys.argv: port_data = int(sys.argv[sys.argv.index('-p') + 1]) else: port_data = DEFAULT_PORT if port_data < 1024 or port_data > 65535: raise ValueError except IndexError: print('After the parameter -\'p\' must be specified the port' ' number.') sys.exit(1) except ValueError: print('Only a number between 1024 and 65535 can be specified' ' as a port.') sys.exit(1) try: if '-a' in sys.argv: addr_data = sys.argv[sys.argv.index('-a') + 1] else: addr_data = '' except IndexError: print('After the parameter \'a\'- must be specified the address' ' that the server will listen to.') sys.exit(1) transfer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transfer.bind((addr_data, port_data)) transfer.listen(MAX_CONNECTIONS) while True: client_socket_box, client_addr = transfer.accept() try: message_receiver = get_message(client_socket_box) print(message_receiver) response = analyzer(message_receiver) send_message(client_socket_box, response) client_socket_box.close() except (ValueError, json.JSONDecodeError): print('Incorrect message was received from client side') client_socket_box.close()
def server_launcher(): """ loading params of cmd, if they're not set, will defined by default, after that function detects addr which will be listen, prepares port and starts to receive a information """ parser = argument_parser() namespace = parser.parse_args(sys.argv[1:]) addr_data = namespace.a port_data = namespace.p if not 1023 < port_data < 65536: SERVER_SIDE_LOGGER.critical( f'port was defined incorrectly {port_data} ' f'port address range available between 1024 and 65535') sys.exit(1) SERVER_SIDE_LOGGER.info( f'server launched, port to connect: {port_data} ' f'connection from address: {addr_data} ' f'(If address not defined, connection will be available without it)') transfer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transfer.bind((addr_data, port_data)) transfer.listen(MAX_CONNECTIONS) while True: client_socket_box, client_addr = transfer.accept() SERVER_SIDE_LOGGER.info(f'connection with {client_addr} stabilized') try: message_receiver = get_message(client_socket_box) SERVER_SIDE_LOGGER.debug(f'message received: {message_receiver}') response = analyzer(message_receiver) SERVER_SIDE_LOGGER.info(f'response is ready to sent: {response}') send_message(client_socket_box, response) SERVER_SIDE_LOGGER.debug( f'connection with client {client_addr} will be closed') client_socket_box.close() except (ValueError, json.JSONDecodeError): SERVER_SIDE_LOGGER.error( f'json file, received from {client_addr} ' f'was unavailable to decode. Connection will be closed') client_socket_box.close() except UnreadableReceivedDataError: SERVER_SIDE_LOGGER.error( f'message format from {client_addr} is unreadable' f'the connection will be closed')
def analyzer(message, message_list, client, clients, names): """ A message handler from clients that accepts a dictionary - message from the client, checks the correctness, returns a response dictionary for a client """ SERVER_SIDE_LOGGER.debug(f'analyze of client message {message}') if ACTION in message and message[ACTION] == PRESENCE and\ TIME in message and USER in message: if message[USER][ACCOUNT_NAME] not in names.keys(): names[message[USER][ACCOUNT_NAME]] = client send_message(client, RESPONSE_200) else: response = RESPONSE_400 response[ERROR] = 'name of user is occupied' send_message(client, response) clients.remove(client) client.close() return elif ACTION in message and message[ACTION] == MESSAGE and \ DESTINATION in message and TIME in message \ and SENDER in message and MESSAGE_TEXT in message: message_list.append(message) return elif ACTION in message and message[ACTION] == EXIT \ and ACCOUNT_NAME in message: clients.remove(names[message[ACCOUNT_NAME]]) names[message[ACCOUNT_NAME]].close() del names[message[ACCOUNT_NAME]] return else: response = RESPONSE_400 response[ERROR] = 'bad request' send_message(client, response) return
def process_message(message, names, listen_socks): """ The function of addressing a message to a specific client. Accepts a message dictionary, list of registered users and listening sockets. Returns nothing. :param message: :param names: :param listen_socks: :return: """ if message[DESTINATION] in names and names[ message[DESTINATION]] in listen_socks: send_message(names[message[DESTINATION]], message) SERVER_SIDE_LOGGER.info( f'Message was sent to user {message[DESTINATION]} ' f'from user {message[SENDER]}.') elif message[DESTINATION] in names and names[ message[DESTINATION]] not in listen_socks: raise ConnectionError else: SERVER_SIDE_LOGGER.error( f'Пользователь {message[DESTINATION]} не зарегистрирован на сервере, ' f'отправка сообщения невозможна.')
def analyzer(client_message, message_list, client): """ A message handler from clients that accepts a dictionary - message from the client, checks the correctness, returns a response dictionary for a client """ SERVER_SIDE_LOGGER.debug(f'analyze of client message {client_message}') if ACTION in client_message and client_message[ACTION] == PRESENCE \ and TIME in client_message and USER in client_message \ and client_message[USER][ACCOUNT_NAME] == 'Anonymous': send_message(client, {RESPONSE: 200}) return elif ACTION in client_message and client_message[ACTION] == MESSAGE and \ TIME in client_message and MESSAGE_TEXT in client_message: message_list.append((client_message[ACCOUNT_NAME], client_message[MESSAGE_TEXT])) return else: send_message(client, { RESPONSE: 400, ERROR: 'Bad Request' }) return
def client_initializer(): """ Loading param of cmd and do init of socket with transfer by result """ parser = argument_parser() namespace = parser.parse_args(sys.argv[1:]) server_addr = namespace.addr server_port = namespace.port if not 1023 < server_port < 65536: CLIENT_SIDE_LOGGER.critical( f'attempt to launch client with wrong port: {server_port} ' f'allowed ports between 1024 and 65535 numbers, process ends') sys.exit(1) CLIENT_SIDE_LOGGER.info( f'client application was launched with parameters: ' f'server address: {server_addr} ' f'port: {server_port}') try: transfer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transfer.connect((server_addr, server_port)) confirm_presence_to_server = launch_presence() send_message(transfer, confirm_presence_to_server) answer = server_response(get_message(transfer)) CLIENT_SIDE_LOGGER.info(f'received response from server {answer}') print(answer) except json.JSONDecodeError: CLIENT_SIDE_LOGGER.error(f'failed to decode received Json string') except RequestedFieldAbsentError as missing_error: CLIENT_SIDE_LOGGER.error(f'in server response has no requested field' f'{missing_error}{missing_field}') except ConnectionRefusedError: CLIENT_SIDE_LOGGER.critical( f'connection to server is unavailable {server_addr}:{server_port}, ' f'request to connect was refused')
def user_online(from_socket, client_name): """ function coop with user, requests commands, sends msgs :param from_socket: :param client_name: :return: """ helper() while True: command = input('Enter command: ') if command == 'm': message_writer(from_socket, client_name) elif command == 'h': helper() elif command == 'x': send_message(from_socket, command_to_exit(client_name)) print('connection will be closed') CLIENT_SIDE_LOGGER.info(f'process will be closed by user request') time.sleep(0.8) break else: print('Unknown command, please try again.\n' 'To call help(show list of useful commands)' 'Enter - h : ')
def server_launcher(): """ loading params of cmd, if they're not set, will defined by default, after that function detects addr which will be listen, prepares port and starts to receive a information """ addr_to_listen, port_to_listen = argument_parser() SERVER_SIDE_LOGGER.info( f'server launched, port to connect: {port_to_listen} ' f'connection from address: {addr_to_listen} ' f'(If address not defined, connection will be available without it)' ) transfer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transfer.bind((addr_to_listen, port_to_listen)) transfer.settimeout(0.5) clients_online = [] messages_queue = [] transfer.listen(MAX_CONNECTIONS) while True: try: client_socket_box, addr_to_listen = transfer.accept() except OSError: pass else: SERVER_SIDE_LOGGER.info(f'connection with PC {addr_to_listen} stabilized') clients_online.append(client_socket_box) data_list_for_receiving = [] data_list_to_send = [] data_list_of_err_occured = [] try: if clients_online: data_list_for_receiving, data_list_to_send, data_list_of_err_occured = \ select.select(clients_online, clients_online, [], 0) except OSError: pass if data_list_for_receiving: for msg_by_client in data_list_for_receiving: try: analyzer(get_message(msg_by_client), messages_queue, msg_by_client) except: SERVER_SIDE_LOGGER.info(f'client {msg_by_client.getpeername()} ' f'disconnected from server') clients_online.remove(msg_by_client) if messages_queue and data_list_to_send: message = { ACTION: MESSAGE, SENDER: messages_queue[0][0], TIME: time.time(), MESSAGE_TEXT: messages_queue[0][1] } del messages_queue[0] for client_from_queue in data_list_to_send: try: send_message(client_from_queue, message) except: SERVER_SIDE_LOGGER.info(f' client {client_from_queue.getpeername()} ' f'disconnected from server') clients_online.remove(client_from_queue)