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)
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()
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)