From d021f699735ae55c1362f50064ec115ee367627b Mon Sep 17 00:00:00 2001 From: Daniel Frey Date: Mon, 3 Dec 2018 15:22:38 -0800 Subject: [PATCH 1/5] added server and client code --- client.py | 23 ++++++++++++ server.py | 87 ++++++++++++++++++++++++++++++++++++++++++++ tcp_chat/__init__.py | 0 tcp_echo/__init__.py | 0 4 files changed, 110 insertions(+) create mode 100644 client.py create mode 100644 server.py delete mode 100644 tcp_chat/__init__.py delete mode 100644 tcp_echo/__init__.py diff --git a/client.py b/client.py new file mode 100644 index 0000000..e386548 --- /dev/null +++ b/client.py @@ -0,0 +1,23 @@ +import random +import uuid + + +class Client: + """ + """ + + def __init__(self, conn=None, addr=None): + self.id = str(uuid.uuid4()) + self.nick = f'user_{random.random()}' + self.conn = conn + self.addr = addr + + def __str__(self): + pass + + def __repr__(self): + pass + + def change_nickname(self, nick): + self.nick = nick + return(self) diff --git a/server.py b/server.py new file mode 100644 index 0000000..f31786a --- /dev/null +++ b/server.py @@ -0,0 +1,87 @@ +from client import Client +import threading +import socket + +PORT = 4547 + + +class ChatServer(threading.Thread): + def __init__(self, port, host='localhost'): + super().__init__(daemon=True) + self.port = PORT + self.host = host + self.server = socket.socket( + socket.AF_INET, + socket.SOCK_STREAM, + socket.IPPROTO_TCP, + ) + self.client_pool = [] + + try: + self.server.bind((self.host, self.port)) + except socket.error: + print(f'bind failed { socket.error }') + + self.server.listen(10) + + def parser(self, id, nick, conn, message): + """Parse the message out to look for keywords.""" + if message.decode().startswith('/'): + data = message.decode().split(maxsplit=1) + + if data[0] == '/quit': + conn.sendall(b'You have left the chat.') + reply = nick.encode() + b'has left the channel.\n' + [c.conn.sendall(reply) for c in self.client_pool if len(self.client_pool)] + self.client_pool = [c for c in self.client_pool if c.id != id] + conn.close() + + if data[0] == '/list': + reply = '' + for c in self.client_pool: + reply += c.nick + ' \n' + [c.conn.sendall(reply.encode()) for c in self.client_pool if len(self.client_pool)] + return('') + + else: + reply = nick.encode() + b': ' + message + [c.conn.sendall(reply) for c in self.client_pool if len(self.client_pool)] + return('') + + def run_thread(self, id, nick, conn, addr): + """ changes the nickname and establishes connection""" + print('{} connected with {}:{}'.format(nick, addr[0], str(addr[1]))) + try: + while True: + data = conn.recv(4096) + parsed_nickname = self.parser(id, nick, conn, data) + if len(parsed_nickname): + nick = parsed_nickname + except (ConnectionResetError, BrokenPipeError, OSError): + conn.close() + + def run(self): + """ Runs the server.""" + print('Server running on {}'.format(PORT)) + while True: + conn, addr = self.server.accept() + client = Client(conn, addr) + self.client_pool.append(client) + threading.Thread( + target=self.run_thread, + args=(client.id, client.nick, client.conn, client.addr), + daemon=True + ).start() + + def exit(self): + """Close the connection.""" + self.server.close() + + +if __name__ == '__main__': + server = ChatServer(PORT) + try: + server.run() + except KeyboardInterrupt: + [c.conn.close() for c in server.client_pool if len(server.client_pool)] + server.exit() diff --git a/tcp_chat/__init__.py b/tcp_chat/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tcp_echo/__init__.py b/tcp_echo/__init__.py deleted file mode 100644 index e69de29..0000000 From 657e3e237d7b67b5ba30655aeec39c38b72b4db9 Mon Sep 17 00:00:00 2001 From: Daniel Frey Date: Mon, 3 Dec 2018 16:42:11 -0800 Subject: [PATCH 2/5] list quit and name change are working --- server.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/server.py b/server.py index f31786a..edd72bf 100644 --- a/server.py +++ b/server.py @@ -2,7 +2,7 @@ import threading import socket -PORT = 4547 +PORT = 4556 class ChatServer(threading.Thread): @@ -43,21 +43,29 @@ def parser(self, id, nick, conn, message): [c.conn.sendall(reply.encode()) for c in self.client_pool if len(self.client_pool)] return('') + if data[0] == '/nickname': + for i in self.client_pool: + if i.id == id: + i.change_nickname(data[1]) + reply = 'Nickname updated to' + data[1] + return reply + else: reply = nick.encode() + b': ' + message [c.conn.sendall(reply) for c in self.client_pool if len(self.client_pool)] return('') def run_thread(self, id, nick, conn, addr): - """ changes the nickname and establishes connection""" + """changes the nickname and establishes connection""" print('{} connected with {}:{}'.format(nick, addr[0], str(addr[1]))) try: while True: data = conn.recv(4096) - parsed_nickname = self.parser(id, nick, conn, data) - if len(parsed_nickname): - nick = parsed_nickname - except (ConnectionResetError, BrokenPipeError, OSError): + parsed_nick = self.parser(id, nick, conn, data) + if len(parsed_nick): + nick = parsed_nick + + except (OSError): conn.close() def run(self): From b86aebbe31f6b929660fa3708647667eb846e7ee Mon Sep 17 00:00:00 2001 From: Daniel Frey Date: Mon, 3 Dec 2018 16:51:51 -0800 Subject: [PATCH 3/5] message changes --- server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server.py b/server.py index edd72bf..ba8a9b5 100644 --- a/server.py +++ b/server.py @@ -2,7 +2,7 @@ import threading import socket -PORT = 4556 +PORT = 4559 class ChatServer(threading.Thread): @@ -47,7 +47,7 @@ def parser(self, id, nick, conn, message): for i in self.client_pool: if i.id == id: i.change_nickname(data[1]) - reply = 'Nickname updated to' + data[1] + reply = 'Nickname updated to:' + data[1] return reply else: From 69010413533eb2ed5658869593a912a5cfe895c1 Mon Sep 17 00:00:00 2001 From: Joyce Liao Date: Mon, 3 Dec 2018 17:12:10 -0800 Subject: [PATCH 4/5] dm --- server.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server.py b/server.py index ba8a9b5..1ebca9c 100644 --- a/server.py +++ b/server.py @@ -2,7 +2,7 @@ import threading import socket -PORT = 4559 +PORT = 4562 class ChatServer(threading.Thread): @@ -50,6 +50,13 @@ def parser(self, id, nick, conn, message): reply = 'Nickname updated to:' + data[1] return reply + if data[0] == '/dm': + for i in self.client_pool: + data = message.decode().split(maxsplit=2) + if i.nick.rstrip() == data[1]: + i.conn.sendall(data[2].encode()) + return(i.nick) + else: reply = nick.encode() + b': ' + message [c.conn.sendall(reply) for c in self.client_pool if len(self.client_pool)] From 1b6bdbaf78a8b9c5c535c8ba0062ea94b093c6bc Mon Sep 17 00:00:00 2001 From: Joyce Liao Date: Mon, 3 Dec 2018 17:18:47 -0800 Subject: [PATCH 5/5] updated README --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f963561..91b2c6e 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,17 @@ Version: 1.0.0 ## OVERVIEW - +With the python standard socket module, enable the user(s) to communicate with the terminal and other users via the local host. Allow each user to execuse commands such as "/quick", "/list", "/nickname ", and "dm ". ## GETTING STARTED - +To successfully execute this program, the user needs Python 3.6, a terminal that runs server.py and additional terminal to connect to the local host as users. ## ARCHITECTURE - +1. server.py: Setup the server on the local host +2. client.py: Setup the client, initiating the client with their conn and addr +3. Via terminal local host, connect the server with the user(s) ## API