Beispiel #1
0
class ClientAsync:

    BUFFER = 1024
    port = Port('_port')

    def __init__(self, loop, addr, port, response_handler):
        self.loop = loop
        self.addr = addr
        self.port = port
        self.response_handler = response_handler
        self.response_decoder = lambda x: x.decode()
        self.request_encoder = lambda x: x.encode()

    def set_response_decoder(self, value):
        self.response_decoder = value

    def set_request_encoder(self, value):
        self.request_encoder = value

    def start_background(self):
        back_lamd = lambda: self.loop.run_until_complete(self.__setup_back())
        self.back_thread = threading.Thread(target=back_lamd, daemon=True)
        self.back_thread.start()

    async def __setup_back(self):
        try:
            asyncio.set_event_loop(self.loop)
            await self.connect()
            await asyncio.gather(self.send(), self.read())
        except Exception as e:
            print(e)

    async def connect(self):
        self.reader, self.writer = \
            await asyncio.open_connection(self.addr, self.port, loop=self.loop)
        print('\rconnected\n')

    async def read(self):
        while True:
            try:
                resp = await self.reader.read(self.BUFFER)
                resp = self.response_decoder(resp)
                print(f'RESPONCE: {resp}')
                self.response_handler(resp)
            except Exception as e:
                print(f'ER: {e}')
                return

    async def send(self):
        while True:
            req = await self.input_.get()
            print(f'REQUEST: {req}')
            req = self.request_encoder(req)
            self.writer.write(req)
            await self.writer.drain()
class ServerAsync:

    BUFFER = 1024
    port = Port('_port')

    def __init__(self, loop, bind_addr, port, request_handler):
        self.loop = loop
        self.addr = bind_addr
        self.port = port
        self.request_handler = request_handler
        self.request_decoder = lambda x: x.decode()
        self.response_encoder = lambda x: x.encode()

    def set_request_decoder(self, value):
        self.request_decoder = value

    def set_response_encoder(self, value):
        self.response_encoder = value

    async def start(self):
        self.server_cor = \
            asyncio.start_server(self.connection_handler, self.addr, self.port, loop=self.loop)
        self.server = await self.server_cor

    def start_background(self):
        back_lamd = lambda: self.loop.run_until_complete(self.__setup_back())
        self.back_thread = threading.Thread(target=back_lamd, daemon=True)
        self.back_thread.start()

    async def __setup_back(self):
        asyncio.set_event_loop(self.loop)
        await self.start()
        await self.server.wait_closed()

    async def connection_handler(self, reader, writer):
        while True:
            try:
                data = await reader.read(self.BUFFER)
                addr = writer.get_extra_info('peername')

                data = self.request_decoder(data)
                print(f'{addr} REQUEST: {data}')
                resp = self.request_handler(data)

                print(f'RESPONCE: {resp}')
                resp = self.response_encoder(resp)
                writer.write(resp)
                await writer.drain()
            except Exception as e:
                print(e)
                return

    def stop(self):
        self.server.close()
Beispiel #3
0
class Server(threading.Thread, metaclass=ServerVerifier):
    port = Port()
    address = Host()

    def __init__(self, server_address, server_port, connections, database):
        self.address = server_address
        self.port = server_port
        self.connections = connections
        self.database = database
        super().__init__()
        self.clients = []
        self.messages = []
        self.names = dict()

    def init_socket(self):
        SERV = socket(AF_INET, SOCK_STREAM)
        try:
            SERV.bind((self.address, self.port))
        except OSError as err:
            print(err)
        SERV_LOG.debug('Запуск сервера')
        SERV.settimeout(0.5)
        self.sock = SERV
        self.sock.listen(self.connections)
        print(f'Сервер запущен {self.address}:{self.port}')

    def run(self):
        self.init_socket()
        while True:
            try:
                client, client_addr = self.sock.accept()
            except OSError:
                pass
            else:
                SERV_LOG.info(f'Подключился клиент: {client_addr}')
                self.clients.append(client)

            read_clients_lst = []
            send_clients_lst = []
            err_lst = []
            try:
                if self.clients:
                    read_clients_lst, send_clients_lst, err_lst = select(
                        self.clients, self.clients, [], 0)
            except OSError:
                pass
            if read_clients_lst:
                for client_with_message in read_clients_lst:
                    try:
                        self.do_answer(get_message(client_with_message),
                                       self.messages, client_with_message,
                                       self.clients, self.names)
                    except Exception:
                        SERV_LOG.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 mess in self.messages:
                try:
                    self.do_message(mess, self.names, send_clients_lst)
                except Exception:
                    SERV_LOG.info(f'Связь с клиентом {mess[TO]} потеряна')
                    self.clients.remove(self.names[mess[TO]])
                    self.database.user_logout(mess[TO])
                    del self.names[mess[TO]]
            self.messages.clear()

    # @log
    def do_answer(self, message, message_list, client, clients, names):
        """Обрабатывает сообщение от клиента и готовит ответ"""
        global new_connection
        SERV_LOG.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 names.keys():
                names[message[USER][ACCOUNT_NAME]] = client
                client_ip, client_port = client.getpeername()
                self.database.user_login(message[USER][ACCOUNT_NAME],
                                         client_ip, client_port)
                send_message(client, {RESPONSE: 200})
                with conflag_lock:
                    new_connection = True
                SERV_LOG.debug('Ответ подготовлен: {RESPONSE: 200}')
            else:
                SERV_LOG.debug("Ответ подготовлен: {RESPONSE: 400\nERROR: \
'Имя пользователя уже занято'}")
                send_message(client, {
                    RESPONSE: 400,
                    ERROR: 'Имя пользователя уже занято'
                })
                clients.remove(client)
                client.close()
            return

        elif ACTION in message and message[ACTION] == MSG and TIME in message \
                and MESSAGE in message and TO in message:
            message_list.append(message)
            self.database.process_user_message(message[SENDER], message[TO])
            return

        elif ACTION in message and message[ACTION] == QUIT and \
                ACCOUNT_NAME in message:
            self.database.user_logout(message[ACCOUNT_NAME])
            SERV_LOG.debug('Удален клиент из таблицы активных клиентов')
            clients.remove(names[message[ACCOUNT_NAME]])
            names[message[ACCOUNT_NAME]].close()
            del names[message[ACCOUNT_NAME]]
            with conflag_lock:
                new_connection = True
            return

        elif ACTION in message and message[ACTION] == GET_CONTACTS and \
                USER in message and self.names[message[USER]] == client:
            response = {RESPONSE: 202}
            response[LIST_INFO] = self.database.get_contacts(message[USER])
            send_message(client, response)
            SERV_LOG.info(f'Отправлен ответ на запрос GET_CONTACTS {response}')

        elif ACTION in message and message[ACTION] == ADD_CONTACT and \
                ACCOUNT_NAME in message and USER in message \
                and self.names[message[USER]] == client:
            self.database.add_contact(message[USER], message[ACCOUNT_NAME])
            send_message(client, {RESPONSE: 200})

        elif ACTION in message and message[ACTION] == REMOVE_CONTACT and \
            ACCOUNT_NAME in message and USER in message and \
                self.names[message[USER]] == client:
            self.database.remove_contact(message[USER], message[ACCOUNT_NAME])
            send_message(client, {RESPONSE: 200})

        elif ACTION in message and message[ACTION] == USERS_REQUEST and \
            ACCOUNT_NAME in message and self.names[message[ACCOUNT_NAME]] ==\
                client:
            response[LIST_INFO] = [
                user[0] for user in self.database.users_list()
            ]
            send_message(client, response)
            SERV_LOG.info(
                f'Отправлен ответ на запрос USERS_REQUEST {response}')
        else:
            SERV_LOG.debug("Ответ подготовлен: {RESPONSE: 400\nERROR: \
'Bad request'}")
            send_message(client, {RESPONSE: 400, ERROR: 'Bad request'})
            return

    @log
    def do_message(self, message, names, socks):
        """Отправляет сообщение определённому клиенту"""
        if message[TO] in names and names[message[TO]] in socks:
            send_message(names[message[TO]], message)
            SERV_LOG.info(f'Отправлено сообщение пользователю {message[TO]} '
                          f' от пользователя {message[SENDER]}')
        elif message[TO] in names and names[message[TO]] not in socks:
            raise ConnectionError
        else:
            SERV_LOG.error(f'Пользователь {message[TO]} не зарегистрирован на \
на сервере. Отправка невозможна')
Beispiel #4
0
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} , адрес, с которого принимаются подключения:'
            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 run(self):
        # Инициализация сокета
        self.init_socket()

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

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

            # Принимаем сообщения и если ошибка, исключаем клиента.
            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):
                        # Ищем клиента в словаре клиентов и удаляем его из него и базы подключённых
                        logger.info(
                            f'Клиент {client_with_message.getpeername()} отключился от сервера.'
                        )
                        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)

            # Если есть сообщения, обрабатываем каждое
            for message in self.messages:
                try:
                    self.process_message(message, send_data_lst)
                except:
                    logger.info(
                        f'Связь с клиентом с именем {message["to"]} была потеряна'
                    )
                    self.clients.remove(self.names[message['to']])
                    self.database.user_logout(message['to'])
                    del self.names[message['to']]
            self.messages.clear()

    # Функция адресной отправки сообщения определённому клиенту. Принимает словарь - сообщение, список зарегистрированых
    # пользователей и слушающие сокеты. Ничего не возвращает.
    def process_message(self, message, listen_socks):
        if message['to'] in self.names and self.names[
                message['to']] in listen_socks:
            send_message(self.names[message['to']], message)
            logger.info(
                f'Отправлено сообщение пользователю {message["to"]} от пользователя {message["from"]}.'
            )
            print(
                f'Отправлено сообщение пользователю {message["to"]} от пользователя {message["from"]}.'
            )
        elif message['to'] in self.names and self.names[
                message['to']] not in listen_socks:
            raise ConnectionError
        else:
            logger.error(
                f'Пользователь {message["to"]} не зарегистрирован на сервере, отправка сообщения невозможна.'
            )

    # Обработчик сообщений от клиентов, принимает словарь - сообщение от клиента, проверяет корректность, отправляет
    # словарь-ответ в случае необходимости.
    def process_client_message(self, message, client):
        global new_connection
        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
                client_ip, client_port = client.getpeername()
                self.database.user_login(message['user']['account_name'],
                                         client_ip, client_port)
                send_message(client, {'response': 200})
                with conflag_lock:
                    new_connection = True

            else:
                response = {
                    'response': 400,
                    'error': 'Имя пользователя уже занято'
                }
                send_message(client, response)
                self.clients.remove(client)
                client.close()
            return

        # Если это сообщение, то добавляем его в очередь сообщений. Ответ не требуется.
        elif 'action' in message and message['action'] == 'message' and 'to' in message and 'time' in message \
                and 'from' in message and 'mess_text' in message and self.names[message['from']] == client:
            self.messages.append(message)
            self.database.process_message(message['from'], message['to'])
            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']
            with conflag_lock:
                new_connection = True
            return

        # Если это запрос контакт-листа
        elif 'action' in message and message['action'] == 'get_contacts' and 'user' in message and \
            self.names[message['user']] == client:
            response = {'response': 202, 'data_list': None}
            response['data_list'] = self.database.get_contacts(message['user'])
            send_message(client, response)

    # Если это добавление контакта
        elif 'action' in message and message['action'] == 'add' and 'account_name' in message and 'user' in message \
             and self.names[message['user']] == client:
            self.database.add_contact(message['user'], message['account_name'])
            send_message(client, {'response': 200})

    # Если это удаление контакта
        elif 'action' in message and message['action'] == 'remove' and 'account_name' in message and 'user' in message \
            and self.names[message['user']] == client:
            self.database.remove_contact(message['user'],
                                         message['account_name'])
            send_message(client, {'response': 200})

    # Если это запрос известных пользователей
        elif 'action' in message and message['action'] == 'get_users' and 'account_name' in message \
             and self.names[message['account_name']] == client:
            response = {'response': 202}
            response['data_list'] = [
                user[0] for user in self.database.users_list()
            ]
            send_message(client, response)

        # Иначе отдаём Bad request
        else:
            response = {'response': 400, 'error': 'Запрос некорректен'}
            send_message(client, response)
            return
Beispiel #5
0
class Client:
    """ Class client connection and data exchange """

    __slots__ = ('addr', '_port', 'user', 'logger', 'socket', 'connected',
                 'get_chat_sended', 'listener', 'sender', 'encryptors',
                 'priv_key', 'storage', 'subs', 'answers', 'file_answers',
                 'executor', 'execute_queue')

    TCP = (AF_INET, SOCK_STREAM)
    port = Port('_port')

    def __init__(self, addr, port):
        self.logger = logging.getLogger(log_config.LOGGER_NAME)
        self.addr = addr
        self.port = port
        self.connected = self.get_chat_sended = False
        self.subs = {}
        # self.subs = {201: [], 202: [], 203: [], 204: [], 205: []}
        self.answers = queue.Queue()
        self.file_answers = queue.Queue()
        self.execute_queue = queue.Queue()
        self.encryptors = {}

    @property
    def username(self):
        return self.user.username

    def get_encryptor(self, contact):
        return self.encryptors[contact] if contact in self.encryptors else None

    def set_encryptor(self, contact, value):
        self.encryptors[contact] = value

    def set_user(self, username, password):
        self.user = User(username, password)
        self.storage = ClientStorage(username)

    def start(self):
        """ Method start connection to server """

        self.socket = socket(*self.TCP)
        start_txt = f'Connect to {self.addr}:{self.port} as {self.username}'
        self.logger.debug(start_txt)
        print(start_txt)
        self.__connect()

    @try_except_wrapper
    def __connect(self):
        self.socket.connect((self.addr, self.port))
        self.connected = True
        print('Done')
        response = self.authorization()
        if response.code != OK:
            show_error(str(response.message))
            self.logger.warning(response)
            exit()

        self.listener = ClientThread(self.__listen_server, self.logger)
        self.listener.start()

        self.executor = ClientThread(self.__execute, self.logger)
        self.executor.start()

    def __execute(self):
        self.logger.debug('EXECUTER STARTED')
        while self.connected:
            func, *args = self.execute_queue.get()
            self.logger.debug(f'{func} | {args}')
            try:
                func(*args)
            except Exceptiona as e:
                self.logger.error(e)

    @try_except_wrapper
    def __send_request(self, request):
        if not self.connected:
            return
        self.logger.debug(request)
        send_data(self.socket, request)

    @try_except_wrapper
    def __get_response(self):
        if not self.connected:
            return None
        response = get_data(self.socket)
        self.logger.debug(response)
        return response

    @try_except_wrapper
    def authorization(self):
        """ Method of authorization on server """
        pr_req = Request(RequestAction.PRESENCE, self.user.username)
        self.__send_request(pr_req)
        resp = self.__get_response()
        if resp is None:
            return Response(SERVER_ERROR)
        if resp.code != AUTH:
            return resp
        enc_pass = encrypt_rsa(import_pub_key(resp.message.encode()),
                               self.user.password)
        auth_req = Request(RequestAction.AUTH, enc_pass.decode())
        self.__send_request(auth_req)
        return self.__get_response()

    def get_chat_req(self, contact):
        """ Method send request for gets all messages of chat with contact """
        if self.get_chat_sended:
            return
        req = Request(RequestAction.COMMAND,
                      f'get_chat {self.user.username} {contact}')
        self.__send_request(req)

    def get_users_req(self):
        """ Method send request for gets users online """

        self.__send_request(Request(RequestAction.COMMAND, 'get_users'))

    def get_contacts_req(self):
        """ Method send request for gets list contacts """

        self.__send_request(Request(RequestAction.COMMAND, 'get_contacts'))

    @try_except_wrapper
    def start_chat(self, contact):
        """ Method initialization of chat with contact """

        key = self.storage.get_key(contact)
        if key is not None:
            self.set_encryptor(contact, ClientCrypt(key))

        prv, pub = gen_keys()
        self.priv_key = prv
        msg = Msg(pub.export_key().decode(), self.username, contact)
        start_req = Request(RequestAction.START_CHAT, msg)
        self.__send_request(start_req)

    @try_except_wrapper
    def accepting_chat(self, resp_mes):
        """ Method of handle request to start chat """

        r_msg = Msg.from_formated(resp_mes)
        pub = import_pub_key(r_msg.text.encode())

        key = self.storage.get_key(r_msg.sender)
        if key is not None:
            encryptor = ClientCrypt(key)
        else:
            encryptor = ClientCrypt.gen_secret(self.username, r_msg.sender)
            self.storage.add_chat_key(r_msg.sender, encryptor.secret)
        self.set_encryptor(r_msg.sender, encryptor)
        enc_key = encrypt_rsa(pub, encryptor.secret)
        msg = Msg(enc_key.decode(), self.username, r_msg.sender)
        accept_req = Request(RequestAction.ACCEPT_CHAT, msg)
        self.__send_request(accept_req)

    @try_except_wrapper
    def accepted_chat(self, resp_mes):
        """ Method of handle response about confirm start of chat """

        msg = Msg.from_formated(resp_mes)
        encryptor = self.get_encryptor(msg.sender)
        if encryptor is not None:
            return
        secret = decrypt_rsa(self.priv_key, msg.text.encode())
        self.set_encryptor(msg.sender, ClientCrypt(secret))
        self.storage.add_chat_key(msg.sender, secret)

    def add_contact(self, contact):
        """ Method add contact in contact list """

        if self.storage.get_contact(contact):
            return False
        self.storage.add_contact(contact)
        req = Request(RequestAction.COMMAND, f'add_contact {contact}')
        self.__send_request(req)
        return True

    def rem_contact(self, contact):
        """ Method remove contact from contact list """

        self.storage.remove_contact(contact)
        req = Request(RequestAction.COMMAND, f'rem_contact {contact}')
        self.__send_request(req)

    def sync_contacts(self, contacts):
        """ Method sync of list contact from server to client """

        for contact in contacts:
            self.storage.append_contact(contact)

    @try_except_wrapper
    def save_avatar(self, avatar_bytes):
        self.storage.set_avatar(avatar_bytes)
        self.send_avatar(avatar_bytes)

    @property
    def avatar(self):
        value = self.storage.get_avatar(self.username)
        if value:
            return value.avatar

    @avatar.setter
    def avatar(self, value):
        self.storage.set_avatar(self.username, value)
        self.send_avatar_async(value)

    def send_avatar_async(self, avatar_bytes):
        sender_thread = ClientThread(lambda: self.send_avatar(avatar_bytes),
                                     self.logger)
        sender_thread.start()

    @try_except_wrapper
    def send_avatar(self, avatar_bytes):
        avatar_part = 512
        for i in range(0, len(avatar_bytes), avatar_part):
            part = b64encode(avatar_bytes[i:i + avatar_part])
            part_req = Request(RequestAction.IMAGE, part.decode())
            self.__send_request(part_req)
            self.file_answers.get()

        self.logger.debug(f'Send end part')
        end_req = Request(RequestAction.END_IMAGE, 'set_avatar')
        self.__send_request(end_req)
        resp = self.file_answers.get()

    @try_except_wrapper
    def check_self_avatar(self):
        user = self.username
        av_hash = self.storage.get_avatar_hash(self.username)
        ch_req = Request(RequestAction.COMMAND,
                         f'check_avatar {user} {av_hash}')
        self.__send_request(ch_req)
        resp = self.answers.get()
        if not resp:
            self.send_avatar(self.avatar)

    @try_except_wrapper
    def get_user_avatar(self, user):
        avatar = self.storage.get_avatar(user)
        if avatar:
            ch_req = Request(RequestAction.COMMAND,
                             f'check_avatar {user} {avatar.avatar_hash}')
            self.__send_request(ch_req)
            resp = self.answers.get()
            if resp == 1:
                return avatar.avatar
        get_req = Request(RequestAction.GET_IMAGE, user)
        self.__send_request(get_req)
        avatar_bytes = b''

        while True:
            resp = self.file_answers.get()
            if not resp:
                break
            avatar_bytes += b64decode(resp.encode())

        self.storage.set_avatar(user, avatar_bytes)
        return avatar_bytes
        pass

    @try_except_wrapper
    def send_msg(self, text, to):
        """ Method send messge to server """

        if to != '@ALL':
            encryptor = self.get_encryptor(to)
            text = encryptor.encript_msg(text.encode()).decode()
        else:
            text = b64encode(text.encode()).decode()
        msg = Msg(text, self.user, to)
        if to != '@ALL':
            self.storage.add_message(msg.to, msg.text)
        request = Request(RequestAction.MESSAGE, msg)
        self.__send_request(request)

    def __listen_server(self):
        """ Method listen responses from server """

        while self.connected:
            resp = get_data(self.socket)
            self.logger.debug(resp)
            if resp.type != RESPONSE:
                self.logger.warning(f'Received not RESPONSE:\n {resp}')
                continue
            if resp.code == ANSWER:
                self.answers.put(resp.message)
            elif resp.code == FILE_ANSWER:
                self.file_answers.put(resp.message)
            elif resp.code in self.subs.keys():
                for sub in self.subs[resp.code]:
                    # sub(resp.message)
                    self.execute_queue.put((sub, resp.message))
            # else:
            #     self.logger.debug(resp.message)

    @try_except_wrapper
    def get_collection_response(self):
        result = []
        while True:
            resp = self.answers.get(timeout=60)
            if not resp:
                break
            result.append(resp)
        return result

    def subscribe(self, code, func):
        """ Method subscribe of function to response code """

        if code in self.subs.keys():
            self.subs[code].append(func)
        else:
            self.subs[code] = [func]

    def parse_recv_message(self, msg):
        """ Method parse/decrypt message """

        msg = Msg.from_formated(msg)
        if msg.to != '@ALL':
            encryptor = self.get_encryptor(msg.sender)
            msg.text = encryptor.decrypt_msg(msg.text.encode()).decode()
        else:
            msg.text = b64decode(msg.text).decode()
        return msg.sender, msg.text, msg.to
Beispiel #6
0
class Server(metaclass=ServerMaker):
    port = Port()

    def __init__(self, args):
        self.addr = args.a
        self.port = args.p

    def process_request(self, request):
        LOG.debug('process_client_msg: %s', request)

        response = get_jim_response_error(400, 'Bad request')
        msg = None

        if not (ACTION in request and TIME in request):
            pass
        elif request[ACTION] == ACTION_PRESENCE:
            if (USER in request and request[USER][ACCOUNT_NAME] == 'Guest'):
                response = get_jim_response(200)
        elif request[ACTION] == ACTION_MSG:
            if (TO in request and FROM in request and ENCODING in request
                    and MESSAGE in request):
                response = get_jim_response(200)
                msg = request

        return response, msg

    def read_requests(self, r_clients, all_clients):
        requests = {}
        for client_socket in r_clients:
            try:
                data = get_message(client_socket)
                requests[client_socket] = data
            except Exception as e:
                print(
                    f'Клиент {client_socket.fileno()} {client_socket.getpeername()} отключился'
                )
                all_clients.remove(client_socket)

        return requests

    def write_requests(self, requests, w_clients, all_clients, messages_list):
        for client_socket in requests:
            request_data = requests[client_socket]
            response, msg = self.process_request(request_data)
            if msg:
                messages_list.append(msg)

            if client_socket in w_clients:
                try:
                    send_message(client_socket, response)
                except Exception as e:
                    print(
                        f'Клиент {client_socket.fileno()} {client_socket.getpeername()} отключился'
                    )
                    client_socket.close()
                    all_clients.remove(client_socket)

    def write_messages(self, msg_list, w_clients, all_clients):
        msg_list_copy = deepcopy(msg_list)
        for msg in msg_list_copy:
            for client_socket in w_clients:
                try:
                    send_message(client_socket, msg)
                except Exception as e:
                    print(
                        f'Клиент {client_socket.fileno()} {client_socket.getpeername()} отключился'
                    )
                    client_socket.close()
                    all_clients.remove(client_socket)
            msg_list.remove(msg)

    def main_loop(self, ):
        LOG.debug('create socket')
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as transport:
            transport.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            LOG.debug('bind socket to %(a)s:%(p)s', self.addr, self.port)
            transport.bind((self.addr, self.port))

            LOG.debug('start listening')
            transport.listen(MAX_CONNECTIONS)
            transport.settimeout(0.2)

            clients = []
            msg_list = []

            while True:
                try:
                    client, client_addr = transport.accept()
                except OSError as e:
                    pass  # timeout
                else:
                    LOG.info(f'Получен запрос на соединение от {client_addr}')
                    clients.append(client)
                finally:
                    if not clients:
                        continue

                    r = []
                    w = []
                    try:
                        r, w, _ = select.select(clients, clients, [], 0)
                    except Exception as e:
                        print(f'Кто-то отключился\n{e}')

                    if not r:
                        continue
                    requests = self.read_requests(r, clients)
                    self.write_requests(requests, w, clients, msg_list)
                    self.write_messages(msg_list, w, clients)
Beispiel #7
0
class Server(metaclass=ServerVerifier):
    port = Port()
    address = Host()

    def __init__(self, server_address, server_port, connections, database):
        self.address = server_address
        self.port = server_port
        self.connections = connections
        self.database = database

    def __call__(self):
        SERV_LOG.debug('Запуск сервера')
        SERV = socket(AF_INET, SOCK_STREAM)
        while True:
            try:
                SERV.bind((self.address, self.port))
                print(f'Сервер запущен {self.address}:{self.port}')
                break
            except OSError as err:
                print(f'Порт занят. Повторная попытка запуска сервера через 10\
 сек')
                SERV_LOG.debug(f'Ошибка при запуске сервера: {err}')
                sleep(10)
        SERV.listen(self.connections)
        SERV.settimeout(0.5)
        clients = []
        messages = []
        names = dict()
        while True:
            try:
                client, client_addr = SERV.accept()
            except OSError:
                pass
            else:
                SERV_LOG.info(f'Подключился клиент: {client_addr}')
                clients.append(client)

            read_clients_lst = []
            send_clients_lst = []
            err_lst = []
            try:
                if clients:
                    read_clients_lst, send_clients_lst, err_lst = select(
                        clients, clients, [], 0)
            except OSError:
                pass
            if read_clients_lst:
                for client_with_message in read_clients_lst:
                    try:
                        self.do_answer(get_message(client_with_message),
                                       messages, client_with_message, clients,
                                       names)
                    except Exception:
                        SERV_LOG.info(
                            f'Клиент {client_with_message.getpeername()}'
                            f' отключился от сервера')
                        for name in self.names:
                            if names[name] == client_with_message:
                                self.database.user_logout(name)
                                del names[name]
                                break
                        clients.remove(client_with_message)
            for i in messages:
                try:
                    self.do_message(i, names, send_clients_lst)
                except Exception:
                    SERV_LOG.info(f'Связь с клиентом {i[TO]} потеряна')
                    clients.remove(names[i[TO]])
                    self.database.user_logout(i[TO])
                    del names[i[TO]]
            messages.clear()

    @log
    def do_answer(self, message, message_list, client, clients, names):
        """Обрабатывает сообщение от клиента и готовит ответ"""
        SERV_LOG.debug('Обработка сообщения от клиента и подготовка ответа')
        if ACTION in message and message[ACTION] == PRESENCE and TO in message\
                and TIME in message and USER in message:
            if message[USER][ACCOUNT_NAME] not in names.keys():
                names[message[USER][ACCOUNT_NAME]] = client
                client_ip, client_port = client.getpeername()
                self.database.user_login(message[USER][ACCOUNT_NAME],
                                         client_ip, client_port)
                send_message(client, {RESPONSE: 200})
                SERV_LOG.debug('Ответ подготовлен: {RESPONSE: 200}')
            else:
                SERV_LOG.debug("Ответ подготовлен: {RESPONSE: 400\nERROR: \
'Имя пользователя уже занято'}")
                send_message(client, {
                    RESPONSE: 400,
                    ERROR: 'Имя пользователя уже занято'
                })
                clients.remove(client)
                client.close()
            return
        elif ACTION in message and message[ACTION] == MSG and TIME in message \
                and MESSAGE in message and TO in message:
            message_list.append(message)
            return
        elif ACTION in message and message[ACTION] == QUIT and \
                ACCOUNT_NAME in message:
            self.database.user_logout(message[ACCOUNT_NAME])
            clients.remove(names[message[ACCOUNT_NAME]])
            names[message[ACCOUNT_NAME]].close()
            del names[message[ACCOUNT_NAME]]
        else:
            SERV_LOG.debug("Ответ подготовлен: {RESPONSE: 400\nERROR: \
'Bad request'}")
            send_message(client, {RESPONSE: 400, ERROR: 'Bad request'})
            return

    @log
    def do_message(self, message, names, socks):
        """Отправляет сообщение определённому клиенту"""
        if message[TO] in names and names[message[TO]] in socks:
            send_message(names[message[TO]], message)
            SERV_LOG.info(f'Отправлено сообщение пользователю {message[TO]} '
                          f' от пользователя {message[SENDER]}')
        elif message[TO] in names and names[message[TO]] not in socks:
            raise ConnectionError
        else:
            SERV_LOG.error(f'Пользователь {message[TO]} не зарегистрирован на \
на сервере. Отправка невозможна')
class Client:
    __slots__ = ('_addr', '_port', 'logger', 'socket', 'connected', 'listener', 'sender')

    TCP = (AF_INET, SOCK_STREAM)
    USER = User(f'Test{random.randint(0, 1000)}')
    addr = Addr('_addr')
    port = Port('_port')
  

    def __init__(self, addr, port):
        self.logger = logging.getLogger(log_config.LOGGER_NAME)
        self.addr = addr
        self.port = port
        self.connected = False

    def start(self):
        self.socket = socket(*self.TCP)
        start_txt = f'Connect to {self.addr}:{self.port} as {self.USER}...'
        self.logger.debug(start_txt)
        print(start_txt)
        self.__connect()

    @try_except_wrapper
    def __connect(self):
        self.socket.connect((self.addr, self.port))
        self.connected = True
        print('Done')
        print_help()
        response = self.presence()
        if response.code != OK:
            self.logger.warning(response)
            return

        self.listener = ClientThread(self.__listen_server, self.logger)
        self.listener.start()
        self.send_msg()

    @try_except_wrapper
    def __send_request(self, request):
        if not self.connected:
            return
        self.logger.debug(request)
        send_data(self.socket, request)

    @try_except_wrapper
    def __get_response(self):
        if not self.connected:
            return
        response = get_data(self.socket)
        self.logger.debug(response)
        return response

    def presence(self):
        request = Request(RequestAction.PRESENCE, self.USER)
        self.__send_request(request)
        return self.__get_response()

    def send_msg(self):
        while self.connected:
            msg = input('Enter message:\n')
            if msg.upper() == 'Q':
                break
            if msg[0] == '!':
                self.__execute_local_command(msg[1:])
                continue
            if msg[0] == '#':
                request = Request(RequestAction.COMMAND, msg[1:])
            else:
                msg = Msg(msg, self.USER)
                msg.parse_msg()
                request = Request(RequestAction.MESSAGE, msg)
            self.__send_request(request)

    def __execute_local_command(self, command):
        if command == 'help':
            print_help()
        elif command == 'set_name':
            name = input('Set new name')
            self.USER.username = name
            self.__send_request(Request(RequestAction.PRESENCE, self.USER))
        elif command == 'reconnect':
            self.start()
        else:
            print('Command not found')

    def __listen_server(self):
        while self.connected:
            resp = get_data(self.socket)
            self.logger.debug(resp)
            if resp.type != RESPONSE:
                self.logger.warning(f'Received not RESPONSE:\n {resp}')
                continue
            if resp.code == 101:
                print(f'server: {resp.message}')
            else:
                print(resp.message)
Beispiel #9
0
class Client:
    """ Class client connection and data exchange """

    __slots__ = ('_addr', '_port', 'user', 'logger', 'socket', 'connected',
                 'listener', 'sender', 'encryptors', 'priv_key', 'storage',
                 'subs', 'answers')

    TCP = (AF_INET, SOCK_STREAM)
    addr = Addr('_addr')
    port = Port('_port')

    def __init__(self, addr, port):
        self.logger = logging.getLogger(log_config.LOGGER_NAME)
        self.addr = addr
        self.port = port
        self.connected = False
        self.subs = {}
        # self.subs = {201: [], 202: [], 203: [], 204: [], 205: []}
        self.answers = queue.Queue()
        self.encryptors = {}

    @property
    def username(self):
        return self.user.username

    def get_encryptor(self, contact):
        return self.encryptors[contact] if contact in self.encryptors else None

    def set_encryptor(self, contact, value):
        self.encryptors[contact] = value

    def set_user(self, username, password):
        self.user = User(username, password)
        self.storage = ClientStorage(username)

    def start(self):
        """ Method start connection to server """

        self.socket = socket(*self.TCP)
        start_txt = f'Connect to {self.addr}:{self.port} as {self.username}'
        self.logger.debug(start_txt)
        print(start_txt)
        self.__connect()

    @try_except_wrapper
    def __connect(self):
        self.socket.connect((self.addr, self.port))
        self.connected = True
        print('Done')
        response = self.authorization()
        if response.code != OK:
            show_error(str(response.message))
            self.logger.warning(response)
            return

        self.listener = ClientThread(self.__listen_server, self.logger)
        self.listener.start()

    @try_except_wrapper
    def __send_request(self, request):
        if not self.connected:
            return
        self.logger.debug(request)
        send_data(self.socket, request)

    @try_except_wrapper
    def __get_response(self):
        if not self.connected:
            return None
        response = get_data(self.socket)
        self.logger.debug(response)
        return response

    @try_except_wrapper
    def authorization(self):
        """ Method of authorization on server """
        pr_req = Request(RequestAction.PRESENCE, self.user.username)
        self.__send_request(pr_req)
        resp = self.__get_response()
        if resp is None:
            return Response(SERVER_ERROR)
        if resp.code != AUTH:
            return resp
        enc_pass = encrypt_rsa(import_pub_key(resp.message.encode()),
                               self.user.password)
        auth_req = Request(RequestAction.AUTH, enc_pass.decode())
        self.__send_request(auth_req)
        return self.__get_response()

    def get_chat_req(self, contact):
        """ Method send request for gets all messages of chat with contact """

        req = Request(RequestAction.COMMAND,
                      f'get_chat {self.user.username} {contact}')
        self.__send_request(req)

    def get_users_req(self):
        """ Method send request for gets users online """

        self.__send_request(Request(RequestAction.COMMAND, 'get_users'))

    def get_contacts_req(self):
        """ Method send request for gets list contacts """

        self.__send_request(Request(RequestAction.COMMAND, 'get_contacts'))

    @try_except_wrapper
    def start_chat(self, contact):
        """ Method initialization of chat with contact """

        key = self.storage.get_key(contact)
        if key is not None:
            self.set_encryptor(contact, ClientCrypt(key))

        prv, pub = gen_keys()
        self.priv_key = prv
        msg = Msg(pub.export_key().decode(), self.username, contact)
        start_req = Request(RequestAction.START_CHAT, msg)
        self.__send_request(start_req)

    @try_except_wrapper
    def accepting_chat(self, resp_mes):
        """ Method of handle request to start chat """

        r_msg = Msg.from_formated(resp_mes)
        pub = import_pub_key(r_msg.text.encode())

        key = self.storage.get_key(r_msg.sender)
        if key is not None:
            encryptor = ClientCrypt(key)
        else:
            encryptor = ClientCrypt.gen_secret(self.username, r_msg.sender)
            self.storage.add_chat_key(r_msg.sender, encryptor.secret)
        self.set_encryptor(r_msg.sender, encryptor)
        enc_key = encrypt_rsa(pub, encryptor.secret)
        msg = Msg(enc_key.decode(), self.username, r_msg.sender)
        accept_req = Request(RequestAction.ACCEPT_CHAT, msg)
        self.__send_request(accept_req)

    @try_except_wrapper
    def accepted_chat(self, resp_mes):
        """ Method of handle response about confirm start of chat """

        msg = Msg.from_formated(resp_mes)
        encryptor = self.get_encryptor(msg.sender)
        if encryptor is not None:
            return
        secret = decrypt_rsa(self.priv_key, msg.text.encode())
        self.set_encryptor(msg.sender, ClientCrypt(secret))
        self.storage.add_chat_key(msg.sender, secret)

    def add_contact(self, contact):
        """ Method add contact in contact list """

        if self.storage.get_contact(contact):
            return False
        self.storage.add_contact(contact)
        req = Request(RequestAction.COMMAND, f'add_contact {contact}')
        self.__send_request(req)
        return True

    def rem_contact(self, contact):
        """ Method remove contact from contact list """

        self.storage.remove_contact(contact)
        req = Request(RequestAction.COMMAND, f'rem_contact {contact}')
        self.__send_request(req)

    def sync_contacts(self, contacts):
        """ Method sync of list contact from server to client """

        for contact in contacts:
            self.storage.append_contact(contact)

    @try_except_wrapper
    def send_msg(self, text, to):
        """ Method send messge to server """

        encryptor = self.get_encryptor(to)
        text = encryptor.encript_msg(text.encode()).decode()
        msg = Msg(text, self.user, to)
        self.storage.add_message(msg.to, msg.text)
        request = Request(RequestAction.MESSAGE, msg)
        self.__send_request(request)

    def __listen_server(self):
        """ Method listen responses from server """

        while self.connected:
            resp = get_data(self.socket)
            self.logger.debug(resp)
            if resp.type != RESPONSE:
                self.logger.warning(f'Received not RESPONSE:\n {resp}')
                continue
            if resp.code == ANSWER:
                self.answers.put(resp.message)
                print(f'server: {resp.message}')
            elif resp.code in self.subs.keys():
                for sub in self.subs[resp.code]:
                    sub(resp.message)
            else:
                print(resp.message)

    def subscribe(self, code, func):
        """ Method subscribe of function to response code """

        if code in self.subs.keys():
            self.subs[code].append(func)
        else:
            self.subs[code] = [func]

    def parse_recv_message(self, msg):
        """ Method parse/decrypt message """

        msg = Msg.from_formated(msg)
        encryptor = self.get_encryptor(msg.sender)
        msg.text = encryptor.decrypt_msg(msg.text.encode()).decode()
        return msg.sender, msg.text
class Server:
    """ Class implement receive, handle, response to client request """

    __slots__ = ('bind_addr', '_port', 'logger', 'socket', 'clients', 'users',
                 'client_keys', 'listener', 'storage', 'commands',
                 'request_handlers')

    TCP = (AF_INET, SOCK_STREAM)
    TIMEOUT = 5

    port = Port('_port')

    def __init__(self, bind_addr, port):
        self.logger = logging.getLogger(log_config.LOGGER_NAME)
        self.bind_addr = bind_addr
        self.port = port
        self.clients = []
        self.client_keys = {}
        self.users = {}
        self.storage = ServerStorage()
        self.__init_commands()
        self.__init_req_handlers()

    def __init_commands(self):
        """ Method fills dictionary commands """

        self.commands = {
            'get_users': self.storage.get_users_online,
            'add_contact': self.storage.add_contact,
            'rem_contact': self.storage.remove_contact,
            'get_contacts': self.storage.get_contacts,
            'get_chat': self.storage.get_chat_str
        }

    def __init_req_handlers(self):
        """ Method fills dictionary request handlers """

        self.request_handlers = {
            RequestAction.PRESENCE: self.__req_presence_handler,
            RequestAction.AUTH: self.__req_auth_handler,
            RequestAction.QUIT: lambda r, c, *a: self.__client_disconnect(c),
            RequestAction.START_CHAT: self.__req_start_chat_handler,
            RequestAction.ACCEPT_CHAT: self.__req_accept_chat_handler,
            RequestAction.MESSAGE: self.__req_message_handler,
            RequestAction.COMMAND: self.__req_command_handler,
        }

    def start(self, request_count=5):
        """ Method the start configuration server """

        self.socket = socket(*self.TCP)
        self.socket.settimeout(0.5)
        self.socket.bind((self.bind_addr, self.port))
        start_msg = f'Config server port - {self.port}| ' \
                    f'Bind address - {self.bind_addr}'
        self.logger.info(start_msg)

        self.socket.listen(request_count)
        self.listener = ServerThread(self.__listen, self.logger)
        self.listener.start()

    def __listen(self):
        """ Method the listener client connections """

        self.logger.info('Start listen')
        while True:
            try:
                client, addr = self.socket.accept()
            except OSError:
                pass
            except Exception as ex:
                self.logger.error(ex)
            else:
                self.logger.info(f'Connection from {addr}')
                self.clients.append(client)

            i_clients, o_clients = [], []
            try:
                i_clients, o_clients, ex = select(self.clients, self.clients,
                                                  [], self.TIMEOUT)
            except OSError:
                pass
            except Exception as exc:
                self.logger.error(exc)

            requests = self.__get_requests(i_clients)
            if requests:
                self.__send_responses(requests, o_clients)

    @try_except_wrapper
    def __get_requests(self, i_clients):
        """ Method the handler of client requests """

        requests = {}
        for client in i_clients:
            try:
                request = get_data(client)
                requests[client] = request

                if request.action == RequestAction.PRESENCE:
                    if request.body in self.users:
                        requests.pop(client)
                        send_data(client, Response(CONFLICT))
                        self.clients.remove(client)
                    else:
                        self.users[request.body] = client
                elif request.action == RequestAction.QUIT:
                    self.__client_disconnect(client)
            except (ConnectionError, ValueError):
                self.__client_disconnect(client)
            except Exception as e:
                raise e
        return requests

    @try_except_wrapper
    def __send_responses(self, requests, o_clients):
        """ Method the sender of responses to clients """

        for client, i_req in requests.items():
            other_clients = [c for c in o_clients if c != client]
            self.logger.info(client)
            self.logger.info(i_req)
            action = i_req.action
            if action in self.request_handlers:
                self.request_handlers[action](i_req, client, other_clients)
            else:
                self.__send_to_client(client, Response(INCORRECT_REQUEST))
                self.logger.error(f'Incorrect request:\n {i_req}')

    @try_except_wrapper
    def __send_to_client(self, client, resp):
        try:
            self.logger.debug(resp)
            send_data(client, resp)
        except ConnectionError:
            self.__client_disconnect(client)
        except Exception as e:
            raise e

    def __send_to_all(self, clients, resp):
        for cl in clients:
            self.__send_to_client(cl, resp)

    @try_except_wrapper
    def __client_disconnect(self, client):
        self.clients.remove(client)
        user = [u for u, c in self.users.items() if c == client].pop()
        self.users.pop(user)
        self.storage.logout_user(user)
        disconnection_response = Response(DISCONNECTED, user)
        self.logger.debug(disconnection_response)
        for cl in self.clients:
            send_data(cl, disconnection_response)

    def __execute_command(self, command, *args):
        if command in self.commands:
            answer = self.commands[command](*args)
            if answer is False:
                return Response(SERVER_ERROR, 'Command error')
            elif isinstance(answer, list):
                answer = [str(a) for a in answer]
                return Response(ANSWER, answer)
            elif answer is None:
                return Response(ANSWER, 'Done')
            return Response(ANSWER, answer)
        else:
            return Response(INCORRECT_REQUEST, 'Command not found')

    # region Request handlers

    @try_except_wrapper
    def __req_presence_handler(self, i_req, client, *args):
        """ Mathod the handler presence request """

        prv, pub = gen_keys()
        self.client_keys[i_req.body] = (client, prv)
        self.__send_to_client(client, Response(AUTH,
                                               pub.export_key().decode()))

    @try_except_wrapper
    def __req_auth_handler(self, i_req, client, *args):
        """ Mathod the handler authorization request """

        other_clients = args[0]
        user = [u for u, c in self.users.items() if c == client]
        if len(user) == 0:
            self.logger.warning(f'AUTH: user not found')
            return
        user = user[0]
        cl, key = self.client_keys[user]
        if cl != client:
            self.logger.warning('AUTH: connection sockets not equals')
            return
        password = decrypt_password(key, i_req.body)
        if password is None:
            self.logger.warning('AUTH: decrypt error')
            return
        password_hash = get_hash_password(password, user.encode())
        if not self.storage.authorization_user(user, password_hash):
            self.__send_to_client(client, Response(UNAUTHORIZED))
            self.clients.remove(client)
            self.users.pop(user)
            return
        self.storage.login_user(user, client.getpeername()[0])
        self.__send_to_client(client, Response(OK))
        self.__send_to_all(other_clients, Response(CONNECTED, user))

    @try_except_wrapper
    def __req_start_chat_handler(self, i_req, client, *args):
        """ Mathod the handler start chat request """

        msg = Msg.from_dict(i_req.body)
        if msg.to not in self.users:
            self.logger.warning(f'{msg.to} not found')
            return
        self.__send_to_client(self.users[msg.to],
                              Response(START_CHAT, str(msg)))

    @try_except_wrapper
    def __req_accept_chat_handler(self, i_req, client, *args):
        """ Mathod the handler accept chat request """

        msg = Msg.from_dict(i_req.body)
        if msg.to not in self.users:
            self.logger.warning(f'{msg.to} not found')
            return
        self.__send_to_client(self.users[msg.to],
                              Response(ACCEPT_CHAT, str(msg)))

    @try_except_wrapper
    def __req_message_handler(self, i_req, client, *args):
        """ Mathod the handler message request """

        other_clients = args[0]
        msg = Msg.from_dict(i_req.body)
        self.storage.user_stat_update(msg.sender, ch_sent=1)
        if msg.to.upper() != 'ALL' and msg.to in self.users:
            self.storage.user_stat_update(msg.to, ch_recv=1)
            self.storage.add_message(msg.sender, msg.to, msg.text)
            self.__send_to_client(self.users[msg.to],
                                  Response(LETTER, str(msg)))
        else:
            self.__send_to_all(other_clients, Response(LETTER, str(msg)))
            for u in self.storage.get_users_online():
                if str(u) == msg.sender:
                    continue
                self.storage.user_stat_update(str(u), ch_recv=1)
                self.storage.add_message(msg.sender, str(u), msg.text)

    @try_except_wrapper
    def __req_command_handler(self, i_req, client, *args):
        """ Mathod the handler command request """

        command, *args = i_req.body.split()
        user = [u for u, c in self.users.items() if c == client].pop()
        if len(args) < 1 or args[0] != user:
            args.insert(0, user)
        o_resp = self.__execute_command(command, *args)
        self.__send_to_client(client, o_resp)
Beispiel #11
0
class Server:
    __slots__ = ('bind_addr', '_port', 'logger', 'socket', 'clients', 'users',
                 'storage', 'commands', 'listener')

    TCP = (AF_INET, SOCK_STREAM)
    TIMEOUT = 5
    port = Port('_port')

    def __init__(self, bind_addr, port):
        self.logger = logging.getLogger(log_config.LOGGER_NAME)
        self.bind_addr = bind_addr
        self.port = port
        self.clients = []
        self.users = {}
        self.storage = ServerStorage()
        self.__init_commands()

    def __init_commands(self):
        self.commands = {
            'get_users': self.storage.get_users_online,
            'add_contact': self.storage.add_contact,
            'rem_contact': self.storage.remove_contact,
            'get_contacts': self.storage.get_contacts,
        }

    def start(self, request_count=5):
        self.socket = socket(*self.TCP)
        self.socket.settimeout(0.5)
        self.socket.bind((self.bind_addr, self.port))
        self.logger.info(
            f'Config server port - {self.port}| Bind address - {self.bind_addr}'
        )
        self.socket.listen(request_count)
        self.listener = ServerThread(self.__listen, self.logger)
        self.listener.start()
        self.__console()

    def __console(self):
        while True:
            msg = input('Enter command:\n')
            if msg.upper() == 'Q':
                break
            if msg[0] == '#':
                msg = msg[1:]

            command, *args = msg.split(' ')
            if command in self.commands:
                res = self.commands[command](*args)
                print(res)

    def __listen(self):
        self.logger.info('Start listen')
        while True:
            try:
                client, addr = self.socket.accept()
            except OSError:
                pass
            except Exception as ex:
                self.logger.error(ex)
            else:
                self.logger.info(f'Connection from {addr}')
                self.clients.append(client)

            i_clients, o_clients = [], []
            try:
                i_clients, o_clients, ex = select(self.clients, self.clients,
                                                  [], self.TIMEOUT)
            except OSError:
                pass
            except Exception as ex:
                self.logger.error(ex)

            requests = self.__get_requests(i_clients)
            if requests:
                self.__send_responses(requests, o_clients)

    @try_except_wrapper
    def __get_requests(self, i_clients):
        requests = {}
        for client in i_clients:
            try:
                request = get_data(client)
                requests[client] = request

                if request.action == RequestAction.PRESENCE:
                    if request.body in self.users:
                        requests.pop(client)
                        send_data(client, Response(CONFLICT))
                        self.clients.remove(client)
                    else:
                        self.users[request.body] = client
                        self.storage.login_user(request.body,
                                                client.getpeername()[0])
                elif request.action == RequestAction.QUIT:
                    self.__client_disconnect(client)
            except (ConnectionError, ValueError):
                self.__client_disconnect(client)
            except Exception as e:
                raise e
        return requests

    @try_except_wrapper
    def __send_responses(self, requests, o_clients):

        for client, i_req in requests.items():
            other_clients = [c for c in o_clients if c != client]
            self.logger.info(client)
            self.logger.info(i_req)

            if i_req.action == RequestAction.PRESENCE:
                self.__send_to_client(client, Response(OK))
                self.__send_to_all(other_clients,
                                   Response(BASIC, f'{i_req.body} connected'))
            elif i_req.action == RequestAction.QUIT:
                self.__client_disconnect(client)
            elif i_req.action == RequestAction.MESSAGE:
                msg = Msg.from_dict(i_req.body)
                if msg.to.upper() != 'ALL' and msg.to in self.users:
                    self.__send_to_client(self.users[msg.to],
                                          Response(BASIC, str(msg)))
                else:
                    self.__send_to_all(other_clients,
                                       Response(BASIC, str(msg)))
            elif i_req.action == RequestAction.COMMAND:
                command, *args = i_req.body.split()
                user = [u for u, c in self.users.items() if c == client].pop()
                args.insert(0, user)
                # o_resp = Response(ANSWER, self.__execute_command(command, *args))
                o_resp = self.__execute_command(command, *args)
                self.__send_to_client(client, o_resp)
            else:
                self.__send_to_client(client, Response(INCORRECT_REQUEST))
                self.logger.error(f'Incorrect request:\n {i_req}')

    @try_except_wrapper
    def __send_to_client(self, client, resp):
        try:
            send_data(client, resp)
        except ConnectionError:
            self.__client_disconnect(client)
        except Exception as e:
            raise e

    def __send_to_all(self, clients, resp):
        for cl in clients:
            self.__send_to_client(cl, resp)

    @try_except_wrapper
    def __client_disconnect(self, client):
        self.clients.remove(client)
        disconnected_user = [u for u, c in self.users.items()
                             if c == client].pop()
        self.users.pop(disconnected_user)
        self.storage.logout_user(disconnected_user)
        disconnection_response = Response(BASIC,
                                          f'{disconnected_user} disconnected')
        for cl in self.clients:
            send_data(cl, disconnection_response)

    def __execute_command(self, command, *args):
        if command in self.commands:
            answer = self.commands[command](*args)
            if answer is False:
                return Response(SERVER_ERROR, 'Command error')
            elif isinstance(answer, list):
                answer = [str(a) for a in answer]
                return Response(ANSWER, answer)
            elif answer is None:
                return Response(ANSWER, 'Done')
            return Response(ANSWER, answer)
        else:
            return Response(INCORRECT_REQUEST, 'Command not found')
Beispiel #12
0
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} , адрес с которого принимаются подключения:'
            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:
                logger.info(f'Установлено соедение с ПК {client_address}')
                self.clients.append(client)

            recv_data_lst = []
            send_data_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["to"]} была потеряна'
                    )
                    self.clients.remove(self.names[message['to']])
                    del self.names[message['to']]
            self.messages.clear()

    # Функция адресной отправки сообщения определённому клиенту. Принимает словарь сообщение, список зарегистрированых
    # пользователей и слушающие сокеты. Ничего не возвращает.
    def process_message(self, message, listen_socks):
        if message['to'] in self.names and self.names[
                message['to']] in listen_socks:
            send_message(self.names[message['to']], message)
            logger.info(
                f'Отправлено сообщение пользователю {message["to"]} от пользователя {message["from"]}.'
            )
        elif message['to'] in self.names and self.names[
                message['to']] not in listen_socks:
            raise ConnectionError
        else:
            logger.error(
                f'Пользователь {message["to"]} не зарегистрирован на сервере, отправка сообщения невозможна.'
            )

    # Обработчик сообщений от клиентов, принимает словарь - сообщение от клиента, проверяет корректность, отправляет
    # словарь-ответ в случае необходимости.
    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,
                    'error': 'Имя пользователя уже занято'
                }
                send_message(client, response)
                self.clients.remove(client)
                client.close()
            return
        # Если это сообщение, то добавляем его в очередь сообщений. Ответ не требуется.
        elif 'action' in message and message['action'] == 'message' and 'to' in message and 'time' in message \
                and 'from' in message and 'mess_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, 'error': 'Запрос некорректен'}
            send_message(client, response)
            return