def handle_shutdown(client_list):
    for client in client_list:
        if client["viewer_sock"] is not None:
            while True:
                header = chatutils.prepare_message(
                    chatutils.MESSAGE_TYPES["FLW"], chatutils.SRV_ID,
                    client["viewer_id"], chatutils.ERROR_FLAG)
                chatutils.deliver_message(client["viewer_sock"], header)
                try:
                    answer = client["viewer_sock"].recv(chatutils.HEADER_SIZE)
                    message_type = struct.unpack(chatutils.HEADER_FORMAT,
                                                 answer)[0]
                    if message_type == chatutils.MESSAGE_TYPES["OK"]:
                        break
                except:
                    raise
            dettach_client(client, "viewer")

        if client["sender_sock"] is not None:
            while True:
                header = chatutils.prepare_message(
                    chatutils.MESSAGE_TYPES["FLW"], chatutils.SRV_ID,
                    client["sender_id"], chatutils.ERROR_FLAG)
                chatutils.deliver_message(client["sender_sock"], header)
                try:
                    answer = client["sender_sock"].recv(chatutils.HEADER_SIZE)
                    message_type = struct.unpack(chatutils.HEADER_FORMAT,
                                                 answer)[0]
                    if message_type == chatutils.MESSAGE_TYPES["OK"]:
                        break
                except:
                    raise
            dettach_client(client, "sender")

        if not client["viewer_sock"] and not client["sender_sock"]:
            client_list.remove(client)
Exemple #2
0
def viewer(host, port):
    logger = logging.getLogger(__name__)
    viewer_id = 0
    viewer_seq_number = 0
    viewer_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # connect to remote host
    try:
        viewer_sock.connect((host, port))
    except:
        logger.error('Unable to connect')
        sys.exit()

    chatutils.deliver_message(viewer_sock, chatutils.MESSAGE_TYPES["OI"], 0,
                              chatutils.SRV_ID, viewer_seq_number)
    while True:
        try:
            data = viewer_sock.recv(chatutils.HEADER_SIZE)
            message_type, from_id, sender_id, seq_number = struct.unpack(
                chatutils.HEADER_FORMAT, data)
            break
        except:
            continue
    viewer_seq_number += 1
    logger.info("Just received id #%d" % sender_id)

    while True:
        try:
            data = viewer_sock.recv(chatutils.HEADER_SIZE)
            message_type_id, client_from_id, client_to_id, seq_number = struct.unpack(
                chatutils.HEADER_FORMAT, data)
            if message_type_id == chatutils.MESSAGE_TYPES["MSG"]:
                chatutils.deliver_message(viewer_sock,
                                          chatutils.MESSAGE_TYPES["OK"],
                                          viewer_id, client_from_id,
                                          seq_number)

                msg_length = struct.unpack("!H", viewer_sock.recv(2))[0]
                msg_contents = viewer_sock.recv(msg_length)

                if client_to_id == 0:
                    logger.info("BROADCAST: %s" % msg_contents)
                elif client_from_id == chatutils.SRV_ID:
                    logger.info("Server says: %s" % msg_contents)
                else:
                    logger.info("#%d says: %s" %
                                (client_from_id, msg_contents))

            elif message_type_id == chatutils.MESSAGE_TYPES["CLIST"]:
                chatutils.deliver_message(viewer_sock,
                                          chatutils.MESSAGE_TYPES["OK"],
                                          viewer_id, chatutils.SRV_ID,
                                          seq_number)

                clist_size = struct.unpack("!H", viewer_sock.recv(2))[0]
                clist = ''
                for i in range(clist_size):
                    clist = clist + str(
                        struct.unpack("!H", viewer_sock.recv(2))[0])
                    if i < clist_size - 1:
                        clist = clist + ', '

                logger.info("Client list (%d connected clients)" % clist_size)
                print(clist)

            elif message_type_id == chatutils.MESSAGE_TYPES["FLW"]:
                chatutils.deliver_message(viewer_sock,
                                          chatutils.MESSAGE_TYPES["OK"],
                                          viewer_id, chatutils.SRV_ID,
                                          seq_number)
                break
        except KeyboardInterrupt:
            chatutils.deliver_message(viewer_sock,
                                      chatutils.MESSAGE_TYPES["FLW"],
                                      viewer_id, chatutils.SRV_ID,
                                      viewer_seq_number)
            viewer_seq_number += 1
            break

    viewer_sock.close()
Exemple #3
0
def sender(host, port, viewer_id=None):
    logger = logging.getLogger(__name__)
    sender_seq_number = 0
    sender_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # connect to remote host
    try:
        sender_sock.connect((host, port))
    except:
        logger.error('Unable to connect')
        sys.exit()

    chatutils.deliver_message(sender_sock, chatutils.MESSAGE_TYPES["OI"],
                              viewer_id if viewer_id else 9999,
                              chatutils.SRV_ID, sender_seq_number)
    while True:
        try:
            data = sender_sock.recv(chatutils.HEADER_SIZE)
            message_type, from_id, sender_id, seq_number = struct.unpack(
                chatutils.HEADER_FORMAT, data)
            break
        except:
            continue
    sender_seq_number += 1
    logger.info("Just received id #%d" % sender_id)

    def flush():
        sys.stdout.write('Press help to see commands available\nMe (#%d): ' %
                         sender_id)
        sys.stdout.flush()

    flush()
    sock_list = [sender_sock, sys.stdin]
    while True:
        try:
            # Get the list sockets which are readable
            readable, _, _ = select.select(sock_list, [], [], 0)

            for sock in readable:
                if sock == sender_sock:
                    data = sock.recv(chatutils.HEADER_SIZE)
                    message_type, from_id, to_id, seq_number = struct.unpack(
                        chatutils.HEADER_FORMAT, data)
                    if message_type == chatutils.MESSAGE_TYPES["FLW"]:
                        chatutils.deliver_message(
                            sender_sock, chatutils.MESSAGE_TYPES["OK"],
                            sender_id, from_id, seq_number)
                        sys.exit(1)
                else:
                    user_input = raw_input()
                    if user_input.lower() == "help":
                        helper()
                    elif user_input.lower() == 'exit':
                        chatutils.deliver_message(
                            sender_sock, chatutils.MESSAGE_TYPES["FLW"],
                            sender_id, chatutils.SRV_ID, sender_seq_number)
                        sys.exit(2)
                    else:
                        message_split = user_input.split("#")
                        if len(message_split) != 2:
                            logger.warning(
                                "Incorrect message format, type again")
                        else:
                            destination_id, message_contents = message_split
                            destination_id = int(destination_id)
                            if message_contents.lower() == "creq":
                                chatutils.deliver_message(
                                    sender_sock,
                                    chatutils.MESSAGE_TYPES["CREQ"], sender_id,
                                    int(destination_id), sender_seq_number)
                                sender_seq_number += 1

                            elif len(
                                    message_contents) >= chatutils.MAX_MSG_LEN:
                                logger.warning(
                                    "Cannot read more than %d characters, try again with less amount",
                                    chatutils.MAX_MSG_LEN)
                            else:
                                chatutils.deliver_message(
                                    sender_sock,
                                    chatutils.MESSAGE_TYPES["MSG"], sender_id,
                                    int(destination_id), sender_seq_number,
                                    len(message_contents), message_contents)
                                sender_seq_number += 1
                    flush()
        except KeyboardInterrupt:
            chatutils.deliver_message(sender_sock,
                                      chatutils.MESSAGE_TYPES["FLW"],
                                      sender_id, chatutils.SRV_ID,
                                      sender_seq_number)
            sender_seq_number += 1
            break

    sender_sock.close()
def broadcast(client_list, message_type, from_id, seq_number, message):
    for client in client_list:
        if client["viewer_sock"] is not None:
            chatutils.deliver_message(client["viewer_sock"], message_type,
                                      from_id, client["viewer_id"], seq_number,
                                      len(message), message)
def process_message(data,
                    sock,
                    sock_list,
                    client_list,
                    srv_seq_number,
                    viewers_connected=0,
                    senders_connected=0):
    logger = logging.getLogger(__name__)
    message_type_id, client_from_id, client_to_id, seq_number = struct.unpack(
        chatutils.HEADER_FORMAT, data)

    # This message is like a DHCP discover message: "Who can put me into the network?"
    if message_type_id == chatutils.MESSAGE_TYPES["OI"]:
        if client_to_id == chatutils.SRV_ID:

            # Its a viewer
            if client_from_id == 0 and viewers_connected < (
                    chatutils.VIEWER_RANGE_MAX - chatutils.VIEWER_RANGE_MIN):
                viewer_id = attach_client(client_list, "viewer")
                client_list.append({
                    "viewer_id": viewer_id,
                    "viewer_sock": sock,
                    "sender_id": None,
                    "sender_sock": None
                })

                chatutils.deliver_message(sock, chatutils.MESSAGE_TYPES["OK"],
                                          chatutils.SRV_ID, viewer_id,
                                          seq_number)
                log = "A new viewer has arrived, its id is #%d" % viewer_id
                logger.info(log)
                broadcast(client_list, chatutils.MESSAGE_TYPES["MSG"],
                          chatutils.SRV_ID, srv_seq_number, log)
                srv_seq_number += 1
                viewers_connected += 1

            # Its a sender
            elif client_from_id != 0 and senders_connected < (
                    chatutils.SENDER_RANGE_MAX - chatutils.SENDER_RANGE_MIN):

                # Sender is not gonna attach itself to any viewer
                if not get_client_type(client_from_id) == 'viewer':
                    sender_id = attach_client(client_list, "sender")
                    client_list.append({
                        "viewer_id": None,
                        "viewer_sock": None,
                        "sender_id": sender_id,
                        "sender_sock": sock
                    })

                    chatutils.deliver_message(sock,
                                              chatutils.MESSAGE_TYPES["OK"],
                                              chatutils.SRV_ID, sender_id,
                                              seq_number)
                    log = "A new sender has arrived, its id is #%d and its not attached to any viewer" % sender_id
                    logger.info(log)
                    broadcast(client_list, chatutils.MESSAGE_TYPES["MSG"],
                              chatutils.SRV_ID, srv_seq_number, log)
                    srv_seq_number += 1
                    senders_connected += 1

                # Sender already has a predefined viewer
                else:
                    viewer = get_client_by_parameter(client_list, "viewer_id",
                                                     client_from_id)

                    # Viewer incorrect id
                    if viewer is None:
                        chatutils.deliver_message(
                            sock, chatutils.MESSAGE_TYPES["ERRO"],
                            chatutils.SRV_ID, client_from_id, seq_number)

                        log = "Invalid id viewer provided in new sender's attempt"
                        logger.warning(log)
                    else:
                        sender_id = attach_client(client_list, "sender")
                        viewer["sender_id"] = sender_id
                        viewer["sender_sock"] = sock

                        chatutils.deliver_message(
                            sock, chatutils.MESSAGE_TYPES["OK"],
                            chatutils.SRV_ID, sender_id, seq_number)
                        log = "A new sender has arrived, its id is #%d and its attached to viewer #%d" % (
                            sender_id, viewer["viewer_id"])
                        logger.info(log)
                        broadcast(client_list, chatutils.MESSAGE_TYPES["MSG"],
                                  chatutils.SRV_ID, seq_number, log)
                        srv_seq_number += 1
                        senders_connected += 1
        else:
            ''' Possible error causes
            - No more space for viewers or senders
            - Sender provided a bad ID '''
            chatutils.deliver_message(sock, chatutils.MESSAGE_TYPES["ERRO"],
                                      chatutils.SRV_ID, client_from_id,
                                      seq_number)

    # The client wants to disconnect
    elif message_type_id == chatutils.MESSAGE_TYPES["FLW"]:

        if client_to_id == chatutils.SRV_ID and is_valid_client(
                sock, client_list, client_from_id):
            client_type = get_client_type(client_from_id)
            client = get_client_by_parameter(client_list, client_type + "_id",
                                             client_from_id)

            # Checks if client is viewer or sender
            if client_type == 'sender':
                if client["viewer_sock"]:
                    chatutils.deliver_message(client["viewer_sock"],
                                              chatutils.MESSAGE_TYPES["FLW"],
                                              chatutils.SRV_ID,
                                              client["viewer_id"],
                                              srv_seq_number)
                    log = "#%d left" % client["viewer_id"]
                    logger.info(log)

                    sock_list.remove(client["viewer_sock"])
                    dettach_client(client, "viewer")
                    viewers_connected -= 1
                    srv_seq_number += 1

                senders_connected -= 1

            elif client_type == 'viewer':
                viewers_connected -= 1

            chatutils.deliver_message(sock, chatutils.MESSAGE_TYPES["OK"],
                                      chatutils.SRV_ID, client_from_id,
                                      seq_number)
            log = "#%d left" % client_from_id
            logger.info(log)
            broadcast(client_list, chatutils.MESSAGE_TYPES["MSG"],
                      chatutils.SRV_ID, srv_seq_number, log)
            srv_seq_number += 1

            dettach_client(client, client_type)
            sock_list.remove(sock)
            sock.close()

            # If client isn't associated with no more ids and sockets, remove it from client_list
            if client["viewer_id"] is None and client["viewer_sock"] is None \
                    and client["sender_id"] is None and client["sender_sock"] is None:
                client_list.remove(client)
        else:
            ''' Possible error causes
            - Invalid client_from_id '''
            chatutils.deliver_message(sock, chatutils.MESSAGE_TYPES["ERRO"],
                                      chatutils.SRV_ID, client_from_id,
                                      seq_number)

    # The client just sent a message
    elif message_type_id == chatutils.MESSAGE_TYPES["MSG"]:

        if is_valid_client(sock, client_list, client_from_id):
            chatutils.deliver_message(sock, chatutils.MESSAGE_TYPES["OK"],
                                      chatutils.SRV_ID, client_from_id,
                                      seq_number)
            message_len = struct.unpack("!H", sock.recv(2))[0]
            message = sock.recv(message_len)

            if client_to_id == chatutils.SRV_ID:
                logger.info("[MSG #%d]: %s", client_from_id, message)

            # Its a broadcast
            elif client_to_id == 0:
                logger.info("#%d says via broadcast: %s", client_from_id,
                            message)
                broadcast(client_list, chatutils.MESSAGE_TYPES["MSG"],
                          client_from_id, srv_seq_number, message)
                srv_seq_number += 1

            else:
                client_type = get_client_type(client_to_id)
                client_to = get_client_by_parameter(client_list,
                                                    client_type + "_id",
                                                    client_to_id)
                if client_to["viewer_sock"]:
                    chatutils.deliver_message(client_to["viewer_sock"],
                                              chatutils.MESSAGE_TYPES["MSG"],
                                              client_from_id, client_to_id,
                                              seq_number, message_len, message)
                else:
                    chatutils.deliver_message(sock,
                                              chatutils.MESSAGE_TYPES["ERRO"],
                                              chatutils.SRV_ID, client_from_id,
                                              seq_number)
        else:
            log = "client_from_id #%d does not match expected id behind socket" % client_from_id
            logger.error(log)
            chatutils.deliver_message(sock, chatutils.MESSAGE_TYPES["ERRO"],
                                      chatutils.SRV_ID, client_from_id,
                                      seq_number)

    # The client asked for a list of clients
    elif message_type_id == chatutils.MESSAGE_TYPES["CREQ"]:

        if is_valid_client(sock, client_list, client_from_id):
            chatutils.deliver_message(sock, chatutils.MESSAGE_TYPES["OK"],
                                      chatutils.SRV_ID, client_from_id,
                                      seq_number)

            logger.info("#%d asked for the list of connected clients" %
                        client_from_id)
            clist, clist_len = get_clist(client_list)

            # Broadcast CLIST
            if client_to_id == 0:
                broadcast(client_list, chatutils.MESSAGE_TYPES["CLIST"],
                          chatutils.SRV_ID, srv_seq_number, clist)
            else:
                client_to_type = get_client_type(client_to_id)
                client_to = get_client_by_parameter(client_list,
                                                    client_to_type + "_id",
                                                    client_to_id)
                chatutils.deliver_message(client_to["viewer_sock"],
                                          chatutils.MESSAGE_TYPES["CLIST"],
                                          chatutils.SRV_ID, client_to_id,
                                          srv_seq_number, clist_len, clist)
            srv_seq_number += 1
        else:
            ''' Possible error causes
            - Invalid client_from_id
            - client_to_id is not SRV_ID '''
            chatutils.deliver_message(sock, chatutils.MESSAGE_TYPES["ERRO"],
                                      chatutils.SRV_ID, client_from_id,
                                      seq_number)