From aa37cc0aff63cb071a42fd7016cb07c101831b0b Mon Sep 17 00:00:00 2001 From: myitinos Date: Mon, 20 May 2019 13:59:20 +0800 Subject: [PATCH] a step back to use CLI again --- client/ChatConnection.py | 7 ++- client/ChatInterface.py | 103 +++++++++++++------------------------ client/__main__.py | 11 +++- server/ClientConnection.py | 39 ++++++++++---- server/Server.py | 20 +++---- server/__main__.py | 4 +- 6 files changed, 93 insertions(+), 91 deletions(-) diff --git a/client/ChatConnection.py b/client/ChatConnection.py index f4e2da5..cee250c 100644 --- a/client/ChatConnection.py +++ b/client/ChatConnection.py @@ -8,13 +8,17 @@ class ChatConnection(object): self.soc.connect((host, port)) self.buffSize = buffSize self.encoding = encoding + self.isOpen = True def send(self, msg: str) -> None: """ Wrapper method of socket.send and encode the message to set character encoding. """ - self.soc.send(bytes(msg, self.encoding)) + try: + self.soc.send(bytes(msg, self.encoding)) + except OSError: + self.close() def recv(self) -> str: """ @@ -28,3 +32,4 @@ class ChatConnection(object): Wrapper method to close the socket """ self.soc.close() + self.isOpen = False diff --git a/client/ChatInterface.py b/client/ChatInterface.py index ac3ac0c..7e59967 100644 --- a/client/ChatInterface.py +++ b/client/ChatInterface.py @@ -1,94 +1,61 @@ #!/usr/bin/env python3.7 -import tkinter import threading from ChatConnection import ChatConnection class ChatInterface(object): - def __init__(self, connection: ChatConnection): + def __init__(self, + connection: ChatConnection, + name: str = ''): # set connection to use self.connection = connection + self.name = name - # initialise tkinter window - self.window = tkinter.Tk() - self.window.title("Chatter") # window title - # set method to trigger when window is close - self.window.protocol("WM_DELETE_WINDOW", self.close) - # end of window initialisation - - # initialise message frame - self.messagesFrame = tkinter.Frame(self.window) - # # add scrollBar to see previously recovered messages - self.scrollBar = tkinter.Scrollbar(self.messagesFrame) - self.scrollBar.pack(side=tkinter.RIGHT, fill=tkinter.Y) - # # add messagesList to print recovered messages - self.messagesList = tkinter.Listbox( - self.messagesFrame, height=15, width=50, yscrollcommand=self.scrollBar.set) - self.messagesList.pack(side=tkinter.LEFT, fill=tkinter.BOTH) - self.messagesList.pack() - self.messagesFrame.pack() - # end of message frame initialisation - - # initialise input - self.input = tkinter.StringVar() - self.input.set("Type your messages here.") - # # initialise input frame - self.inputFrame = tkinter.Entry(self.window, textvariable=self.input) - self.inputFrame.bind("", self.send) - self.inputFrame.pack() - # # initialise send button - self.sendButton = tkinter.Button( - self.window, text="Send", command=self.send) - self.sendButton.pack() - # end of input initialisation - - def close(self, event=None) -> None: - """ - Set input to ':quit' and trigger send method - """ - self.input.set(":quit") - self.send() - - def send(self, event=None) -> None: + def send(self, msg) -> None: """ Send message to connection """ - msg = self.input.get() + self.connection.send(msg) # check if input is quit command - if msg != ':quit': - # clear the input bar - self.input.set('') - # output the client message to message list - self.messagesList.insert(tkinter.END, '[YOU] {}'.format(msg)) - # send the message to connection - self.connection.send(msg) - else: - # close the connection and window - self.connection.close() - self.window.quit() + if self.connection.isOpen: + if msg[0] == '/': + cmd = msg[1:5] + if cmd == 'quit': + self.connection.close() + elif cmd == 'name': + tmp = msg.split() + if len(tmp) == 2: + self.name = tmp[1] def listener(self) -> None: """ Listener method to keep receiving new message from connection """ - while True: - try: - # receive messages from connection - msg = self.connection.recv() - # add recevied message to message list - self.messagesList.insert(tkinter.END, msg[:-1]) - except OSError: - break + while self.connection.isOpen: + # receive messages from connection + msg = self.connection.recv() + if len(msg) > 0: + # add recevied message to message list + print('\r' + + msg + + '\n' + + '[ME({})]: '.format(self.name), end='') + else: + self.connection.close() def start(self) -> None: - """ - Start background listener and tkinter.mainloop - """ # initialise background listener as daemon thread backgroundListener = threading.Thread(target=self.listener) backgroundListener.daemon = True backgroundListener.start() - # start tkinter main loop and open the GUI window - tkinter.mainloop() + self.send('/name {}'.format(self.name)) + + while self.connection.isOpen: + try: + msg = input('[ME({})]: '.format(self.name)) + except (EOFError, KeyboardInterrupt): + msg = '/quit' + finally: + self.send(msg) diff --git a/client/__main__.py b/client/__main__.py index bf4a016..31b6bb0 100644 --- a/client/__main__.py +++ b/client/__main__.py @@ -15,6 +15,15 @@ if __name__ == "__main__": metavar="PORT", help='Host PORT to connect to.', type=int) + parser.add_argument("--name", + metavar="NAME", + help='Name you want to be identified as.', + default='', + type=str) args = parser.parse_args() - ChatInterface(ChatConnection(args.host, args.port)).start() + try: + ChatInterface(ChatConnection(args.host, args.port), + name=args.name).start() + except ConnectionRefusedError as e: + print(e) diff --git a/server/ClientConnection.py b/server/ClientConnection.py index 6d53a84..d5ceffe 100644 --- a/server/ClientConnection.py +++ b/server/ClientConnection.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3.7 -from Server import Server +import Server class ClientConnection(object): @@ -10,28 +10,49 @@ class ClientConnection(object): def __init__(self, server: Server, conn, addr, buffSize=4096, encoding='utf8'): self.server = server self.conn = conn - self.addr = addr - self.name = '{}:{}'.format(addr[0], addr[1]) + self.addr = '{}:{}'.format(addr[0], addr[1]) + self.name = '' self.buffSize = buffSize self.encoding = encoding + def nameFormat(self): + return '[{}({})]'.format(self.name, self.addr) + def serve(self) -> None: """ - Server this connection and start lisneting for incoming + Serve this connection and start listening for incoming message """ self.send( - '[system] Type \':quit\' to quit this conversation') + '[system] You are logged in as [{}({})]\n'.format(self.name, self.addr) + + '[system] Type \'/quit\' to quit this conversation\n' + + '[system] Type \'/name \' to change your name') while True: msg = self.recv() - if msg != ':quit' and len(msg) > 0: - self.server.sendToAll(self, msg) + if len(msg) > 0: + if msg[0] == '/': + cmd = msg[1:5] + if cmd == 'quit': + self.send('[system] :goodbye:') + break + elif cmd == 'name': + tmp = msg.split() + if len(tmp) == 2: + self.server.sendToAll( + self, + "changed name to {}".format(tmp[1])) + self.name = tmp[1] + else: + self.send('[system] whitespace are not allowed') + else: + self.send('[system] unrecognised command') + else: + self.server.sendToAll(self, msg) else: - self.send('[system] :goodbye:') break self.kill() - def send(self, msg: str, end='\n') -> None: + def send(self, msg: str, end='') -> None: """ Wrapper method for socker.send and encode the message to set encoding diff --git a/server/Server.py b/server/Server.py index d8430a2..fbc17dc 100644 --- a/server/Server.py +++ b/server/Server.py @@ -2,8 +2,7 @@ import socket # socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR import logging import threading - -from ClientConnection import ClientConnection +import ClientConnection class Server(object): @@ -30,18 +29,19 @@ class Server(object): self.host, self.port, self.maxClients)) while True: conn, addr = self.soc.accept() - client = ClientConnection(self, conn, addr) - msg = '{}:{} has joined the chat.'.format( - client.addr[0], client.addr[1]) + client = ClientConnection.ClientConnection(self, conn, addr) + msg = '{} has joined the chat.'.format(client.addr) self._announce(msg) self.clients.append(client) - threading.Thread(target=client.serve).start() + t = threading.Thread(target=client.serve) + t.daemon = True + t.start() - def removeClient(self, client: ClientConnection) -> None: + def removeClient(self, client: ClientConnection.ClientConnection) -> None: """ Remove client from this server list of connected clients """ - msg = '{}:{} has left the chat.'.format(client.addr[0], client.addr[1]) + msg = '{} has left the chat.'.format(client.nameFormat()) self.clients.remove(client) self._announce(msg) @@ -54,11 +54,11 @@ class Server(object): for client in self.clients: client.send(msg) - def sendToAll(self, sender: ClientConnection, msg: str) -> None: + def sendToAll(self, sender: ClientConnection.ClientConnection, msg: str) -> None: """ Broadcast message from one client to other clients """ - msg = '[{}:{}] {}'.format(sender.addr[0], sender.addr[1], msg) + msg = '{} {}'.format(sender.nameFormat(), msg) logging.info(msg) for client in self.clients: if client != sender: diff --git a/server/__main__.py b/server/__main__.py index 523f0ee..f9fbeb2 100644 --- a/server/__main__.py +++ b/server/__main__.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3.7 import logging -from Server import Server +import Server def initLogging(logFileName: str) -> None: @@ -46,6 +46,6 @@ if __name__ == "__main__": initLogging(args.logFile) try: - Server(args.host, args.port).run() + Server.Server(args.host, args.port).run() except KeyboardInterrupt: logging.info('Stopped by user input')