You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

98 lines
2.7 KiB
Python

# -*- 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))