class Server(threading.Thread, metaclass=ServerMaker): port = Port() def __init__(self, listen_address, listen_port, database): self.addr = listen_address self.port = listen_port self.database = database self.clients = [] self.messages = [] self.names = dict() super().__init__() def init_socket(self): logger.info( f'Запущен сервер, с портом: {self.port} , адрес с которого принимаются подключения: {self.addr}. Если адрес не указан, принимаются соединения с любых адресов.') # Готовим сокет forward = socket.socket(socket.AF_INET, socket.SOCK_STREAM) forward.bind((self.addr, self.port)) forward.settimeout(0.5) # Начинаем слушать сокет. self.sock = forward self.sock.listen() def run(self): self.init_socket() while True: try: client, client_address = self.sock.accept() except OSError: pass else: logger.info(f'Установлено соедение с ПК {client_address}') self.clients.append(client) recv_data_lst = [] send_data_lst = [] err_lst = [] try: if self.clients: recv_data_lst, send_data_lst, err_lst = select.select(self.clients, self.clients, [], 0) except OSError: pass if recv_data_lst: for client_with_message in recv_data_lst: try: self.handling_mess_from_client(rec_message(client_with_message), client_with_message) except: logger.info(f'Клиент {client_with_message.getpeername()} отключился от сервера.') self.clients.remove(client_with_message) for message in self.messages: try: self.process_message(message, send_data_lst) except: logger.info(f'Связь с клиентом с именем {message[DESTINATION]} была потеряна') self.clients.remove(self.names[message[DESTINATION]]) del self.names[message[DESTINATION]] self.messages.clear() def process_message(self, message, listen_socks): """ Отправка сообщения конкретному пользователю :param message: :param names: :param listen_socks: :return: """ if message[DESTINATION] in self.names and self.names[message[DESTINATION]] in listen_socks: transmit_message(self.names[message[DESTINATION]], message) logger.info(f'Отправлено сообщение пользователю {message[DESTINATION]} ' f'от пользователя {message[SENDER]}.') elif message[DESTINATION] in self.names and self.names[message[DESTINATION]] not in listen_socks: raise ConnectionError else: logger.error( f'Пользователь {message[DESTINATION]} не зарегистрирован на сервере, ' f'отправка сообщения невозможна.') def handling_mess_from_client(self, message, client): """ Обработка сообщения от клиента в виде словаря, проверка и отправка ответа. :param message: :param messages_list: :param client: :param clients: :param names: :return: """ logger.debug(f'Разбор сообщения от клиента : {message}') # Если это сообщение presence получаем и отвечаем if ACTION in message and message[ACTION] == PRESENCE and \ TIME in message and USER in message: # Регистрация пользователя если он ещё не зареган # иначе завершаем соединение. if message[USER][ACCOUNT_NAME] not in self.names.keys(): self.names[message[USER][ACCOUNT_NAME]] = client client_ip, client_port = client.getpeername() self.database.user_login(message[USER][ACCOUNT_NAME], client_ip, client_port) transmit_message(client, RESPONSE_200) else: response = RESPONSE_400 response[ERROR] = 'Выберите другое имя - это уже занято.' transmit_message(client, response) self.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: self.messages.append(message) return # Если клиент выходит elif ACTION in message and message[ACTION] == EXIT and ACCOUNT_NAME in message: self.database.user_logout(message[ACCOUNT_NAME]) self.clients.remove(self.names[ACCOUNT_NAME]) self.names[ACCOUNT_NAME].close() del self.names[ACCOUNT_NAME] return else: response = RESPONSE_400 response[ERROR] = 'Запрос некорректен.' transmit_message(client, response) return
class Server(metaclass=ServerMaker): port = Port() def __init__(self, listen_address, listen_port): # Параментры подключения self.addr = listen_address self.port = listen_port # Список подключённых клиентов. self.clients = [] # Список сообщений на отправку. self.messages = [] # Словарь содержащий сопоставленные имена и соответствующие им сокеты. self.names = dict() def init_socket(self): logger.info( f'Запущен сервер, порт для подключений: {self.port} , адрес с которого принимаются подключения: {self.addr}. Если адрес не указан, принимаются соединения с любых адресов.' ) # Готовим сокет transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transport.bind((self.addr, self.port)) transport.settimeout(0.5) # Начинаем слушать сокет. self.sock = transport self.sock.listen() def main_loop(self): # Инициализация Сокета self.init_socket() # Основной цикл программы сервера while True: # Ждём подключения, если таймаут вышел, ловим исключение. try: client, client_address = self.sock.accept() except OSError: pass else: logger.info(f'Установлено соедение с ПК {client_address}') self.clients.append(client) recv_data_lst = [] send_data_lst = [] err_lst = [] # Проверяем на наличие ждущих клиентов try: if self.clients: recv_data_lst, send_data_lst, err_lst = select.select( self.clients, self.clients, [], 0) except OSError: pass # принимаем сообщения и если ошибка, исключаем клиента. 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: logger.info( f'Клиент {client_with_message.getpeername()} отключился от сервера.' ) self.clients.remove(client_with_message) # Если есть сообщения, обрабатываем каждое. for message in self.messages: try: self.process_message(message, send_data_lst) except: logger.info( f'Связь с клиентом с именем {message[DESTINATION]} была потеряна' ) self.clients.remove(self.names[message[DESTINATION]]) del self.names[message[DESTINATION]] self.messages.clear() # Функция адресной отправки сообщения определённому клиенту. Принимает словарь сообщение, список зарегистрированых # пользователей и слушающие сокеты. Ничего не возвращает. def process_message(self, message, listen_socks): if message[DESTINATION] in self.names and self.names[ message[DESTINATION]] in listen_socks: send_message(self.names[message[DESTINATION]], message) logger.info( f'Отправлено сообщение пользователю {message[DESTINATION]} от пользователя {message[SENDER]}.' ) elif message[DESTINATION] in self.names and self.names[ message[DESTINATION]] not in listen_socks: raise ConnectionError else: logger.error( f'Пользователь {message[DESTINATION]} не зарегистрирован на сервере, отправка сообщения невозможна.' ) # Обработчик сообщений от клиентов, принимает словарь - сообщение от клиента, проверяет корректность, отправляет # словарь-ответ в случае необходимости. def process_client_message(self, message, client): logger.debug(f'Разбор сообщения от клиента : {message}') # Если это сообщение о присутствии, принимаем и отвечаем if ACTION in message and message[ ACTION] == PRESENCE and TIME in message and USER in message: # Если такой пользователь ещё не зарегистрирован, регистрируем, иначе отправляем ответ и завершаем соединение. if message[USER][ACCOUNT_NAME] not in self.names.keys(): self.names[message[USER][ACCOUNT_NAME]] = client send_message(client, RESPONSE_200) else: response = RESPONSE_400 response[ERROR] = 'Имя пользователя уже занято.' send_message(client, response) self.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: self.messages.append(message) return # Если клиент выходит elif ACTION in message and message[ ACTION] == EXIT and ACCOUNT_NAME in message: self.clients.remove(self.names[ACCOUNT_NAME]) self.names[ACCOUNT_NAME].close() del self.names[ACCOUNT_NAME] return # Иначе отдаём Bad request else: response = RESPONSE_400 response[ERROR] = 'Запрос некорректен.' send_message(client, response) return
class Server(metaclass=ServerMaker): port = Port() def __init__(self, listen_addr, listen_port): self.addr = listen_addr self.port = listen_port self.clients = [] self.messages = [] self.names = dict() def init_socket(self): LOG.info( f"Запущен сервер, порт для подключений: {self.port} , " f"адрес с которого принимаются подключения: {self.addr}." f"Если адрес не указан, принимаются соединения с любых адресов.") transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transport.bind((self.addr, self.port)) transport.settimeout(0.5) self.sock = transport self.sock.listen() def main_loop(self): self.init_socket() while True: try: client, client_address = self.sock.accept() except OSError: pass else: LOG.info(f"Установлено соедение с ПК {client_address}") self.clients.append(client) recv_data_lst = [] send_data_lst = [] err_lst = [] try: if self.clients: recv_data_lst, send_data_lst, err_lst = select.select( self.clients, self.clients, [], 0) except OSError: pass if recv_data_lst: for client_with_message in recv_data_lst: try: self.client_message_handler( MSG.get(client_with_message), client_with_message) except: LOG.info( f"Клиент {client_with_message.getpeername()} отключился от сервера." ) self.clients.remove(client_with_message) for message in self.messages: try: self.process_message(message, send_data_lst) except: LOG.info( f"Связь с клиентом с именем {message[s.KEY_TO]} была потеряна" ) self.clients.remove(self.names[message[s.KEY_TO]]) del self.names[message[s.KEY_TO]] self.messages.clear() def process_message(self, message, listen_socks): if (message[s.KEY_TO] in self.names and self.names[message[s.KEY_TO]] in listen_socks): MSG.send(self.names[message[s.KEY_TO]], message) LOG.info( f"Отправлено сообщение пользователю {message[s.KEY_TO]} от пользователя {message[s.KEY_FROM]}." ) elif (message[s.KEY_TO] in self.names and self.names[message[s.KEY_TO]] not in listen_socks): raise ConnectionError else: LOG.error( f"Пользователь {message[s.KEY_TO]} не зарегистрирован на сервере, отправка сообщения невозможна." ) def client_message_handler(self, message, client): """ Message handler from clients, accepts dictionary - message from the client, checks correctness, returns the response dictionary for the client """ LOG.debug(f"Разбираем сообщение: {message}") if (s.KEY_ACTION in message and message[s.KEY_ACTION] == s.ACTION_PRESENCE and s.KEY_TIME in message and s.KEY_USER in message): if message[s.KEY_USER][ s.KEY_ACCOUNT_NAME] not in self.names.keys(): self.names[message[s.KEY_USER][s.KEY_ACCOUNT_NAME]] = client MSG.send(client, s.RESPONSE_200) else: response = s.RESPONSE_400 response[s.KEY_ERROR] = "Имя пользователя уже занято." MSG.send(client, response) self.clients.remove(client) client.close() return # Если это сообщение, то добавляем его в очередь сообщений. # Ответ не требуется. elif (s.KEY_ACTION in message and message[s.KEY_ACTION] == s.ACTION_MESSAGE and s.KEY_TIME in message and s.KEY_TO in message and s.KEY_FROM in message and s.KEY_MESSAGE in message): self.messages.append(message) return # Если клиент выходит elif (s.KEY_ACTION in message and message[s.KEY_ACTION] == s.ACTION_EXIT and s.KEY_ACCOUNT_NAME in message): self.clients.remove(self.names[message[s.KEY_ACCOUNT_NAME]]) self.names[message[s.KEY_ACCOUNT_NAME]].close() del self.names[message[s.KEY_ACCOUNT_NAME]] return # Иначе отдаём Bad request else: response = s.RESPONSE_400 response[s.KEY_ERROR] = "Запрос не корректен" MSG.send(client, response) return