From 8027a80196bc59b11f6ae07c4d1bf74d75a1566d Mon Sep 17 00:00:00 2001 From: Xevion Date: Sun, 10 Jan 2021 11:32:19 -0600 Subject: [PATCH] reorganizing oserver.py into proper folder, creating client class for organization and clarity --- constants.py | 2 + helpers.py | 5 ++ server/MainWindow.ui | 120 ------------------------------------------- server/gui.py | 0 server/handler.py | 120 +++++++++++++++++++++++++++++++++++++++++++ server/main.py | 32 ++++++++++++ 6 files changed, 159 insertions(+), 120 deletions(-) delete mode 100644 server/MainWindow.ui delete mode 100644 server/gui.py create mode 100644 server/handler.py diff --git a/constants.py b/constants.py index 8169064..3f81191 100644 --- a/constants.py +++ b/constants.py @@ -1,3 +1,5 @@ +HEADER_LENGTH = 10 + class Requests: REQUEST_NICK = 'REQUEST_NICK' REFRESH_CONNECTIONS_LIST = 'REFRESH_CLIENT_LIST' diff --git a/helpers.py b/helpers.py index bfe83d1..a7382ca 100644 --- a/helpers.py +++ b/helpers.py @@ -32,3 +32,8 @@ def prepare_server_message(nickname: str, message: str, color: str, msgtime: int 'time': msgtime or int(time.time()) } ) + + +def prepare_request(request: str) -> bytes: + """Helper function for creating a request message.""" + return prepare_json({'type': constants.Types.REQUEST, 'request': request}) diff --git a/server/MainWindow.ui b/server/MainWindow.ui deleted file mode 100644 index d836ca3..0000000 --- a/server/MainWindow.ui +++ /dev/null @@ -1,120 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 800 - 651 - - - - MainWindow - - - - - 0 - 0 - - - - - - - Connections - - - - - - - - 0 - 0 - - - - - 16777215 - 80 - - - - - - - - - 0 - 5 - - - - - - - - - 0 - 0 - - - - - 200 - 16777215 - - - - - - - - Chat History - - - - - - - - - 0 - 0 - 800 - 21 - - - - - File - - - - - - - - - - - Connect to... - - - - - Export chat... - - - - - Quit - - - - - - diff --git a/server/gui.py b/server/gui.py deleted file mode 100644 index e69de29..0000000 diff --git a/server/handler.py b/server/handler.py new file mode 100644 index 0000000..ffca070 --- /dev/null +++ b/server/handler.py @@ -0,0 +1,120 @@ +import json +import logging +import random +import socket +import time +import traceback +import uuid +from typing import Any, List + +import constants +import helpers + +logger = logging.getLogger('handler') + + +class Client: + def __init__(self, conn: socket.socket, address: Any, all_clients: List['Client']): + self.conn, self.address = conn, address + self.all_clients = all_clients + + self.id = str(uuid.uuid4()) + self.nickname = self.id[:8] + self.color = random.choice(constants.Colors.ALL) + + self.first_seen = time.time() + self.last_nickname_change = None + self.last_message_sent = None + + def request_nickname(self) -> None: + """Send a request for the client's nickname information.""" + self.conn.send(helpers.prepare_request(constants.Requests.REQUEST_NICK)) + + def receive_message(self) -> Any: + length = int(self.conn.recv(constants.HEADER_LENGTH).decode('utf-8')) + logger.debug(f'Header received - Length {length}') + message = json.loads(self.conn.recv(length).decode('utf-8')) + logger.info(f'Data received/parsed, type: {message["type"]}') + return message + + def send(self, message: bytes) -> None: + """Sends a pre-encoded message to this client.""" + self.conn.send(message) + + def send_message(self, message: str) -> None: + """Sends a string message as the server to this client.""" + self.conn.send(helpers.prepare_server_message( + nickname='Server', message=message, color=constants.Colors.BLACK + )) + + def broadcast_message(self, message: str) -> None: + """Sends a string message to all connected clients as the Server.""" + prepared = helpers.prepare_server_message( + nickname='Server', message=message, color=constants.Colors.BLACK + ) + for client in self.all_clients: + client.send(prepared) + + def broadcast(self, message: bytes) -> None: + """Sends a pre-encoded message to all connected clients""" + for client in self.all_clients: + client.send(message) + + def handle(self) -> None: + while True: + try: + data = self.receive_message() + + if data['type'] == constants.Types.REQUEST: + if data['request'] == constants.Requests.REFRESH_CONNECTIONS_LIST: + self.conn.send(helpers.prepare_json( + { + 'type': constants.Types.USER_LIST, + 'users': [ + { + 'nickname': other.nickname, + 'color': other.color + } for other in self.all_clients + ] + } + )) + elif data['type'] == constants.Types.NICKNAME: + nickname = data['nickname'] + if self.last_nickname_change is None: + logger.info("Nickname is {}".format(nickname)) + self.broadcast(helpers.prepare_server_message( + nickname='Server', + message=f'{nickname} joined!', + color=constants.Colors.BLACK + )) + self.last_nickname_change = time.time() + else: + logger.info(f'{self.nickname} changed their name to {nickname}') + self.nickname = nickname + elif data['type'] == constants.Types.MESSAGE: + self.broadcast(helpers.prepare_server_message( + nickname=self.nickname, + message=data['content'], + color=self.color + )) + + # Basic command processing + if data['content'] == '/reroll': + color = random.choice(constants.Colors.ALL) + colorName = constants.Colors.ALL_NAMES[constants.Colors.ALL.index(color)] + self.color = color + self.broadcast(helpers.prepare_server_message( + nickname='Server', + message=f'Changed your color to {colorName} ({color})', + color=constants.Colors.BLACK + )) + except: + traceback.print_exc() + logger.info(f'Client {self.id} closed. ({self.nickname})') + self.conn.close() + self.broadcast(helpers.prepare_server_message( + nickname='Server', + message=f'{self.nickname} left!', + color=constants.Colors.BLACK + )) + break diff --git a/server/main.py b/server/main.py index e69de29..7806509 100644 --- a/server/main.py +++ b/server/main.py @@ -0,0 +1,32 @@ +import logging +import socket +import threading + +from server import handler + +host = '127.0.0.1' +port = 55555 + +server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +server.bind((host, port)) +server.listen() + +logging.basicConfig(level=logging.DEBUG, + format='[%(asctime)s] [%(levelname)s] [%(threadName)s] %(message)s') +logger = logging.getLogger('server') + +clients = [] + +# Receiving / Listening Function +def receive(): + while True: + # Accept Connection + conn, address = server.accept() + logger.info(f"New connection from {address}") + + client = handler.Client(conn, address, clients) + client.request_nickname() + + # Start Handling Thread For Client + thread = threading.Thread(target=client.handle, name=client.id[:8]) + thread.start()