"""Chat Client for Client/Server programming lab in UC Berkeley's CS61A Protocol Description: Logging In: Startup for 61AChat is a traditional three way handshake of the following format: Client -> Server "Client|Server|hello|" Server -> Client "Server|Client|welcome|" Client -> Server "Client|Server|thanks|" Messaging Another Client: To send a message to another client, one simply sends a "send-msg" message to the Server, which it forwards along to the correct recipient in a "receive-msg": Client1 -> Server "Client1|Client2|send-msg|message here" Server -> Client2 "Client1|Client2|receive-msg|message here" Logging Out: To log out, a client sends the logout message to the server: Client -> Server "Client|Server|logout|" At which point the Server removes the client from the table. In addition to removing the client upon request, we have the Listing users: To get a list of users, the client sends the server a clients-list message. The server then replies with a receive-msg message with a body which lists all users. Client -> Server "Client|Server|clients-list|" Server -> Client "Server|Client|receive-msg|" Written by Tom Magrino """ from ucb import main from socket import socket, error, AF_INET, SOCK_DGRAM from chatcommon import MSG_SIZE_LIMIT, decode_message, Message class ChatClient(object): def __init__(self, username, server_address): self.username = username self.sock = socket(AF_INET, SOCK_DGRAM) self.server_address = server_address self.connected = self.connect() def connect(self): if self.sock.connect_ex(self.server_address) != 0: return False return self.handshake() def receive_message(self, blocking=False): if blocking: self.sock.settimeout(None) else: self.sock.setblocking(0.0) buff = bytearray(b" " * MSG_SIZE_LIMIT) try: self.sock.recv_into(buff) except error as e: buff = bytearray(b" " * MSG_SIZE_LIMIT) data = buff.decode().strip() if data == "": return None return decode_message(data) def send_message(self, message): s = socket(AF_INET, SOCK_DGRAM) s.connect(self.server_address) s.send(bytearray(str(message).encode())) s.close() def handshake(self): hello_msg = Message(self.username, "server", "hello", "") self.sock.send(bytearray(str(hello_msg).encode())) welcome_msg = self.receive_message(blocking=True) if welcome_msg.action == "welcome": thanks_msg = Message(self.username, "server", "thanks", "") self.sock.send(bytearray(str(thanks_msg).encode())) if welcome_msg.action == "welcome": self.connected = True else: print("Something went wrong!:") print(welcome_msg.body) self.sock.close() self.connected = False return self.connected def logout(self): logout_message = Message(self.username, "server", "logout", "") self.connected = False def print_msgs(self): msg_to_me = self.receive_message() if msg_to_me: print("{0} says: {1}".format(msg_to_me.src, msg_to_me.body)) def get_command(self): return input("CS61AChat ~ {0}: ".format(self.username)).split() def im(self, dest, body): msg = Message(self.username, dest, "send-msg", body) self.send_message(msg) def print_help(self): print("""Known Commands: /exit - leave the chat program /who - see who's available to chat /msg [username] - message the person with the given username /help - print this help message""") def chat_repl(self): while self.connected: self.print_msgs() next_command = self.get_command() if len(next_command) <= 0: continue elif next_command[0] == "/exit": self.logout() elif next_command[0] == "/who": self.send_message(Message(self.username, "server", "clients-list", "")) elif next_command[0] == "/msg": dest = next_command[1] body = " ".join(next_command[2:]) self.im(dest, body) elif next_command[0] == "/help": self.print_help() else: print("I'm sorry, I don't know that one!") return @main def run(*args): name = input("Please give a username: ") server_ip = input("Please give the server ip: ") server_port = int(input("Please give the server port: ")) myclient = ChatClient(name, (server_ip, server_port)) myclient.chat_repl()