class ClientHandler: LOGIN_MSG = 'Choose a nickname.' HELLO_MSG = 'You are in, {}!' NICK_TAKEN_MSG = 'Nickname {} is taken, choose another one.' SHUTDOWN_MSG = 'Server shut down and you were disconnected.' def __init__(self, sock, addr, server): self.server = server self.conn = Connection(sock, addr) self.inbox = Queue(maxsize=MAX_CLIENT_QUEUE) self.nickname = None self.channel = None @property def addr(self): return self.conn.addr def init(self): try: self.conn.send_msg(self.LOGIN_MSG) while True: try: nickname = self.conn.recv_msg() self.server.register_client(nickname, self) self.server.addr_client[self.addr] = self self.nickname = nickname self.conn.send_msg(self.HELLO_MSG.format(nickname)) break except NickTakenError: self.conn.send_msg(self.NICK_TAKEN_MSG.format(nickname)) except OSError: self.shutdown() # Connection fully established. self.server.subscribe(self.nickname, 'default') self.channel = self.server.channels['default'] self.in_channel() def shutdown(self): try: self.conn.send_msg(self.SHUTDOWN_MSG) self.conn.close() except OSError: pass # yeah, really self.channel.unsub(self.nickname) self.server.remove_client(self.nickname) def in_channel(self): receiver = threading.Thread(target=self.process_inbox, daemon=True) receiver.start() try: while True: msg = self.conn.recv_msg() msg = '{}: {}'.format(self.nickname, msg) self.channel.publish(('tcp', msg), self.nickname) except ConnectionError: self.shutdown() def process_inbox(self): try: while True: kin, msg = self.inbox.get() if kin == 'udp': send_udp(msg, self.addr) else: self.conn.send_msg(msg) except ConnectionError: self.shutdown() def push(self, msg): self.inbox.put(msg) def handle_udp(self, msg): self.channel.publish(('udp', msg), self.nickname)
class Client: def __init__(self, server_host='127.0.0.1', server_port=8000, client_host='127.0.0.1', client_port=8001): self.server_addr = (server_host, server_port) self.client_addr = (client_host, client_port) self.conn = None def run(self): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(self.client_addr) s.connect(self.server_addr) self.conn = Connection(s, self.server_addr) receiver = threading.Thread(target=self.receive, daemon=True) receiver.start() udp_receiver = threading.Thread(target=self.receive_udp, daemon=True) udp_receiver.start() try: while True: msg = input() if msg == 'U' or msg == 'M': with open('ascii.txt', 'r', encoding='ascii') as ascii_art: content = ascii_art.read() if msg == 'U': addr = self.server_addr else: addr = MULTICAST_ADDR send_udp(content, addr, self.client_addr) else: self.conn.send_msg(msg) except KeyboardInterrupt: print('Received Ctrl+C.') except ConnectionError: print('Connection to server closed.') def receive(self): try: while True: msg = self.conn.recv_msg() print('>>> {}'.format(msg)) except ConnectionError: self.conn.close() def receive_udp(self): with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as u_sock: with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as m_sock: u_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) u_sock.bind(self.client_addr) host = socket.gethostbyname(socket.gethostname()) m_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) m_sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(MULTICAST_ADDR[0]) + socket.inet_aton(host)) m_sock.bind(MULTICAST_ADDR) ev_loop = select.epoll() u_fd, m_fd = u_sock.fileno(), m_sock.fileno() ev_loop.register(u_fd, select.EPOLLIN) ev_loop.register(m_fd, select.EPOLLIN) try: while True: events = ev_loop.poll() for file_no, event in events: if event | select.EPOLLIN: if file_no == u_fd: msg = u_sock.recv(MAX_UDP_SIZE) else: msg, addr = m_sock.recvfrom(MAX_UDP_SIZE) if addr == self.client_addr: continue print(msg.decode()) except ConnectionError: self.conn.close()