Esempio n. 1
0
    def __init__(self, server):
        Thread.__init__(self)

        self.server = server
        self.serializer = Serializer()

        self.bind_addr = ''
        self.bind_port = None
        self.sock = None
        self.__stop = False
Esempio n. 2
0
    def __init__(self, server, sock, addr, number):
        Thread.__init__(self)

        self.server = server

        self.sock = sock
        self.addr = addr
        self.number = number

        self.serializer = Serializer()
        self.last_activity = None
        self.nickname = self.addr
        self.__stop = False
Esempio n. 3
0
    def __init__(self, nickname, server_id):
        self.nickname = nickname
        self.server_id = server_id

        self.serializer = Serializer()

        self.exit = False

        self.server_addr = None

        self.bad_servers = []

        self.msg_sock = None

        self.threads = []

        self.available_servers = []
Esempio n. 4
0
class Read(Thread):
    def __init__(self, client):
        Thread.__init__(self)

        self.client = client
        self.__stop = False
        self.serializer = Serializer()

    def close(self):
        if self.client.msg_sock:
            self.client.msg_sock.close()

    def run(self):
        if not self.client.exit:
            logging.info(msg=' Enter \'exit\' to close connection.')
            logging.info(msg=' Please enter a message.')

        while not self.__stop:
            msg, _, _ = select.select([sys.stdin], [], [], 3)

            time.sleep(.5)

            if not msg and not self.client.msg_sock._closed:
                continue
            elif self.client.msg_sock._closed:
                logging.info(msg=' Sorry you got disconnected')
                return

            if msg:
                msg = sys.stdin.readline().strip()

            if msg == 'exit':
                self.client.exit = True
                self.client.disconnect_from_server()
                return

            try:
                self.client.msg_sock.sendall(
                    self.serializer.serialize({
                        'action': Action.MESSAGE,
                        'nickname': self.client.nickname,
                        'message': msg
                    }))
            except (BrokenPipeError, OSError):
                continue

    def stop(self):
        self.__stop = True
Esempio n. 5
0
class Show(Thread):
    def __init__(self, sock):
        Thread.__init__(self)

        self.sock = sock
        self.__stop = False
        self.serializer = Serializer()

    def close(self):
        if self.sock:
            self.sock.close()

    def run(self):
        while not self.__stop:
            try:
                ready, _, _ = select.select([self.sock], [], [])
            except (OSError, ValueError):
                return

            time.sleep(.5)

            if not ready:
                continue

            try:
                data = self.sock.recv(1024)
                server = self.serializer.deserialize(data)
            except (EOFError, ConnectionResetError):
                self.stop()
                self.close()
                return

            logging.info(msg=f' {server["nickname"]}: {server["message"]}')

        self.sock.close()

    def stop(self):
        self.__stop = True
Esempio n. 6
0
class Broadcast(Thread):
    """
    Each server should respond to broadcasts from known subnets with a
    message that contains following information:

    - Server IP
    - Free slots count
    - Server ID
    """
    def __init__(self, server):
        Thread.__init__(self)

        self.server = server
        self.serializer = Serializer()

        self.bind_addr = ''
        self.bind_port = None
        self.sock = None
        self.__stop = False

    def close(self):
        if self.sock:
            self.sock.close()

    def bind_to_port(self):
        bind_port = None

        for port in Port.BROADCAST_RANGE:
            try:
                self.sock.bind((self.bind_addr, port))
                bind_port = port
                break
            except OSError:
                continue

        return bind_port

    def set_socket(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.bind_port = self.bind_to_port()

        if not self.bind_port:
            self.sock = None
            logging.error(
                msg=f' Could not find valid port to start the server')
            return self.sock

        logging.info(msg=' Wait for client broadcast...')
        logging.info(msg=f' Bind address     : {self.bind_addr}')
        logging.info(msg=f' Bind port        : {self.bind_port}')
        logging.info(msg=f' Server given port: {self.sock.getsockname()[1]}')

        return self.sock

    def run(self):
        if not self.set_socket():
            self.stop()

        while not self.__stop:
            try:
                ready, _, _ = select.select([self.sock], [], [], 2)
            except OSError:
                continue

            if ready:
                data, client_addr = self.sock.recvfrom(1024)
                client_data = self.serializer.deserialize(data)

                logging.info(
                    msg=f' Incoming client broadcast, address: {client_addr}')
                logging.info(
                    msg=f' Incoming client broadcast, data: {client_data}')

                if client_addr[0] not in self.server.addresses:
                    logging.info(msg=f' {client_addr} address not in list of '
                                 f'server subnet addresses.')
                    continue

                self.sock.sendto(
                    self.serializer.serialize({
                        'id':
                        self.server.id,
                        'ip':
                        self.server.host_ip,
                        'slots':
                        self.server.get_slots_available(),
                    }), client_addr)
        self.close()

    def stop(self):
        self.__stop = True
Esempio n. 7
0
    def __init__(self, client):
        Thread.__init__(self)

        self.client = client
        self.__stop = False
        self.serializer = Serializer()
Esempio n. 8
0
    def __init__(self, sock):
        Thread.__init__(self)

        self.sock = sock
        self.__stop = False
        self.serializer = Serializer()
Esempio n. 9
0
class Client:
    def __init__(self, nickname, server_id):
        self.nickname = nickname
        self.server_id = server_id

        self.serializer = Serializer()

        self.exit = False

        self.server_addr = None

        self.bad_servers = []

        self.msg_sock = None

        self.threads = []

        self.available_servers = []

    def disconnect_from_server(self):
        """
        If server gets "disconnect" from
        the client, notification to all other clients should also be sent.
        """
        self.msg_sock.sendall(
            self.serializer.serialize({
                'action': Action.DISCONNECT,
            }))
        self.stop()

    def run(self):
        """
        If connection doesn't occur within allowed time (3 seconds),
        client should wait for 4 seconds and start discovery process again.
        """

        if not self.discover():
            logging.error(msg=' Failed to discover proper servers')
            return

        connected = self.connect()

        while not connected:
            logging.info(msg=' Wait 4 seconds and restart discovery process')
            time.sleep(4)

            if not self.discover():
                logging.error(msg=' Failed to discover proper servers')
                return

            connected = self.connect()

            if connected:
                break

        self.start_threads()
        self.keep_alive()

    def keep_alive(self):
        """
        If client gets disconnected from the server,
        after 4 seconds it should restart discovery process.
        """

        while self.exit is False:
            time.sleep(1)

            if False not in self.threads_alive():
                continue

            self.stop()

            if self.exit:
                return

            while True in self.threads_alive():
                time.sleep(.1)

            logging.info(msg=' Restarting discovery in 4 seconds...')
            time.sleep(4)

            self.run()
            return

    def start_threads(self):
        """
        When client sends a message, it should be sent to all other clients
        connected to the same server (no chat rooms).

        Each client should have a nickname attached to each message.
        Messages should go through the server.

        Client app should get messages from the user via stdin - one line for one message.
        Client app should display each message with nickname
        """

        self.threads = [
            Show(sock=self.msg_sock),
            Read(client=self),
        ]

        for thread in self.threads:
            thread.start()

    def nickname_available(self):
        self.msg_sock.sendall(
            self.serializer.serialize({
                'action': Action.NICKNAME,
                'nickname': self.nickname,
            }))
        data = self.msg_sock.recv(1024)
        nickname_status = self.serializer.deserialize(data)

        if not nickname_status['available']:
            logging.info(msg=f' Sorry nickname \'{self.nickname}\''
                         f' is already taken on server \'{self.server_id}\'')
            self.msg_sock.close()
            self.exit = True
            self.stop()

    def connect(self):
        """
        Client chooses one server by id, stops discovery process and
        connects (TCP/IP) to the selected server.

        Client app should not have any inactivity timeouts.

        When connecting to the server, client should wait no
        longer than 3 seconds for connection establishment to occur.
        """

        max_wait = 3
        connected = False

        for server_addr in self.available_servers:
            try:
                self.msg_sock = socket.socket(socket.AF_INET,
                                              socket.SOCK_STREAM)
                self.msg_sock = socket.create_connection(server_addr,
                                                         timeout=max_wait)
                self.nickname_available()
                connected = True
            except (socket.timeout, ConnectionRefusedError):
                self.bad_servers.append(server_addr)
                continue

            if connected:
                break

        if not connected:
            self.msg_sock = None
            self.available_servers = None

        return connected

    def discover(self):
        """
        Client tries to discover all servers in all known subnets by periodic
        (once in 3 sec) broadcast (UDP/IP) till it finds appropriate server.
        """

        time_period = 3

        bind_addr = ''
        bind_port = 0

        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.bind((bind_addr, bind_port))
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

        logging.info(msg=f' ')
        logging.info(msg=f' Bind address     : {bind_addr}')
        logging.info(msg=f' Bind port        : {bind_port}')
        logging.info(msg=f' Client port   : {sock.getsockname()[1]}')

        for broadcast_port in Port.BROADCAST_RANGE:
            sock.sendto(self.serializer.serialize({'nickname': self.nickname}),
                        ('<broadcast>', broadcast_port))

            try:
                ready, _, _ = select.select([sock], [], [], time_period)
            except KeyboardInterrupt:
                return

            if not ready:
                continue

            data, server_addr = sock.recvfrom(1024)
            server = self.serializer.deserialize(data)

            # Avoid server if it
            # doesn't match following conditions
            server_conditions = [
                server['id'] == self.server_id, server['slots'] > 0,
                server_addr not in self.bad_servers
            ]

            if server['slots'] == 0:
                logging.info(msg=f' {server["id"]} is full.')
                logging.info(msg=f' Looking for other servers...')

            if False not in server_conditions:
                self.available_servers.append(server_addr)
                break

        return self.available_servers

    @handle_threads_alive
    def threads_alive(self):
        pass

    @handle_threads_stop
    def stop(self):
        pass
Esempio n. 10
0
class Client(Thread):
    def __init__(self, server, sock, addr, number):
        Thread.__init__(self)

        self.server = server

        self.sock = sock
        self.addr = addr
        self.number = number

        self.serializer = Serializer()
        self.last_activity = None
        self.nickname = self.addr
        self.__stop = False

    def stop(self):
        self.last_activity = None
        self.__stop = True

    def close(self):
        if self.sock:
            self.sock.close()

    def set_nickname(self, nickname):
        if self.nickname_not_set() and self.nickname_available(nickname):
            self.nickname = nickname
            self.sock.sendall(self.serializer.serialize({
                'available': True,
            }))
            return True
        elif self.nickname_not_set():
            self.sock.sendall(self.serializer.serialize({
                'available': False,
            }))
            return False
        else:
            return True

    def get_nickname(self):
        return self.nickname or self.addr

    def nickname_not_set(self):
        return self.nickname == self.addr

    def nickname_available(self, nickname):
        return nickname not in [
            client.nickname for client in self.server.client_threads
        ]

    def set_last_activity(self):
        self.last_activity = datetime.datetime.now()

        logging.info(msg=f' {self.get_nickname()}:'
                     f' Set last activity to: {self.last_activity}')

    def send(self, content):
        """
        Used to broadcast messages to all connected clients
        """
        self.sock.sendall(self.serializer.serialize(content))

    def run(self):
        self.__stop = False
        self.set_last_activity()

        while not self.__stop:
            try:
                ready, _, _ = select.select([self.sock], [], [])
            except OSError:
                return

            time.sleep(.5)

            if not ready:
                continue

            try:
                data = self.sock.recv(1024)
            except (OSError, ConnectionResetError):
                continue

            try:
                client = self.serializer.deserialize(data)
            except EOFError:
                continue

            self.set_last_activity()
            """
            Server shouldn't allow multiple users with the same nickname
            """
            if client['action'] == Action.NICKNAME:
                if not self.set_nickname(nickname=client['nickname']):
                    return self.server.disconnect_client(client=self)
            """
            If server gets "disconnect" from
            the client, notification to all other clients should also be sent.
            """
            if client['action'] == Action.DISCONNECT:
                self.server.disconnect_client(client=self)
                self.server.send_notification(
                    message=f'{self.get_nickname()} left the server.')
                return

            if client['action'] == Action.MESSAGE:
                logging.info(
                    msg=f' {self.get_nickname()}: {client["message"]}')
                for thr in self.server.client_threads:
                    thr.send(content=client)