# -*- coding: utf-8 -*- import json import socket import sys import threading import model BUFFER_SIZE = 2 ** 10 CLOSING = "Application closing..." CONNECTION_ABORTED = "Connection aborted" CONNECTED_PATTERN = "Client connected: {}:{}" ERROR_ARGUMENTS = "Provide port number as the first command line argument" ERROR_OCCURRED = "Error Occurred" EXIT = "exit" JOIN_PATTERN = "{username} has joined" RUNNING = "Server is running..." SERVER = "SERVER" SHUTDOWN_MESSAGE = "shutdown" TYPE_EXIT = "Type 'exit' to exit>" class Server(object): def __init__(self, argv): self.clients = set() self.listen_thread = None self.port = None self.sock = None self.parse_args(argv) def listen(self): self.sock.listen(1) while True: try: client, address = self.sock.accept() except OSError: print(CONNECTION_ABORTED) return print(CONNECTED_PATTERN.format(*address)) self.clients.add(client) threading.Thread(target=self.handle, args=(client,)).start() def handle(self, client): while True: try: message = model.Message(**json.loads(self.receive(client))) except (ConnectionAbortedError, ConnectionResetError): print(CONNECTION_ABORTED) return if message.quit: client.close() self.clients.remove(client) return print(str(message)) if SHUTDOWN_MESSAGE.lower() == message.message.lower(): self.exit() return self.broadcast(message) def broadcast(self, message): for client in self.clients: client.sendall(message.marshal()) def receive(self, client): buffer = "" while not buffer.endswith(model.END_CHARACTER): buffer += client.recv(BUFFER_SIZE).decode(model.TARGET_ENCODING) return buffer[:-1] def run(self): print(RUNNING) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.bind(("", self.port)) self.listen_thread = threading.Thread(target=self.listen) self.listen_thread.start() def parse_args(self, argv): if len(argv) != 2: raise RuntimeError(ERROR_ARGUMENTS) try: self.port = int(argv[1]) except ValueError: raise RuntimeError(ERROR_ARGUMENTS) def exit(self): self.sock.close() for client in self.clients: client.close() print(CLOSING) if __name__ == "__main__": try: Server(sys.argv).run() except RuntimeError as error: print(ERROR_OCCURRED) print(str(error))