commit a46242999bf494949d7d8afe8472c45648cc0389 Author: Kayomn Date: Thu Sep 30 00:21:18 2021 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c0a4daa --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Editors +.idea/ + +# Caches +__pycache__/ + +# Virtual environments +venv/ diff --git a/client.py b/client.py new file mode 100644 index 0000000..83cd2f6 --- /dev/null +++ b/client.py @@ -0,0 +1,26 @@ +__author__ = "Kieran Osborne" +__version__ = "0.0.1" +__status__ = "Development" + +if (__name__ == "__main__"): + import config + import socket as network + import message + + def init() -> None: + address = (config.host, config.port) + + print("Starting connection to", address) + + with network.socket(network.AF_INET, network.SOCK_STREAM) as socket: + socket.setblocking(False) + socket.connect_ex(address) + + while True: + message_body = input("Enter a message: ") + + print("\n") + socket.send(message.serialize("Kayomn", message_body)) + + init() + diff --git a/config.py b/config.py new file mode 100644 index 0000000..5c8e395 --- /dev/null +++ b/config.py @@ -0,0 +1,6 @@ +__author__ = "Kieran Osborne" +__version__ = "0.0.1" +__status__ = "Development" + +host = "127.0.0.1" +port = 12345 diff --git a/message.py b/message.py new file mode 100644 index 0000000..e50942a --- /dev/null +++ b/message.py @@ -0,0 +1,32 @@ +import sys +import json +import struct + + +class Message: + def __init__(self, author: str, body: str) -> None: + self.author = author + self.body = body + + def serialize(self) -> bytes: + return serialize(self.author, self.body) + + +def deserialize(data: bytes) -> Message: + buffer_length = 2 + header_length = struct.unpack(">H", data[:buffer_length])[0] + header = json.loads(data[buffer_length:header_length]) + + return Message(header["author"], header["encoded-body"]) + + +def serialize(author: str, body: str) -> bytes: + encoding = sys.getdefaultencoding() + + header_bytes = json.dumps({ + "byteorder": sys.byteorder, + "author": author, + "body": encoding, + }).encode("utf-8") + + return (struct.pack(">H", len(header_bytes)) + header_bytes) \ No newline at end of file diff --git a/server.py b/server.py new file mode 100644 index 0000000..1c64d69 --- /dev/null +++ b/server.py @@ -0,0 +1,104 @@ +__author__ = "Kieran Osborne" +__version__ = "0.0.1" +__status__ = "Development" + +if (__name__ == "__main__"): + import config + import traceback + import socket as network + import selectors + import message + + class User: + def __init__(self, user_socket: network.socket): + self.socket = user_socket + self.connection, self.address = user_socket.accept() + self.selector = selectors.DefaultSelector() + + print("Accepted connection from", self.address) + self.connection.setblocking(False) + + def close(self): + self.selector.close() + self.socket.close() + + def read(self): + try: + # Should be ready to read + data = self.socket.recv(4096) + except BlockingIOError: + # Resource temporarily unavailable (errno EWOULDBLOCK) + pass + else: + if data: + print(message.deserialize(data)) + else: + raise RuntimeError("Peer closed.") + + def write(self): + pass + + def run(self): + try: + while True: + events = self.selector.select(timeout=None) + + for (key, mask) in events: + try: + if (mask & selectors.EVENT_READ): + self.read() + + elif (mask & selectors.EVENT_WRITE): + self.write() + + except Exception: + print(f"Exception raised on {self.address}: {traceback.format_exc()}"), + self.socket.close() + + if not self.selector.get_map(): + break + + except KeyboardInterrupt: + print("caught keyboard interrupt, exiting") + + finally: + self.selector.close() + + def init(): + address = (config.host, config.port) + selector = selectors.DefaultSelector() + + with network.socket(network.AF_INET, network.SOCK_STREAM) as socket: + # Avoid bind() exception: OSError: [Errno 48] Address already in use + socket.setsockopt(network.SOL_SOCKET, network.SO_REUSEADDR, 1) + socket.bind(address) + socket.listen() + + print("Listening on", address) + + socket.setblocking(False) + selector.register(socket, selectors.EVENT_READ, data=None) + + users = [] + + try: + while True: + events = selector.select(timeout=None) + + for (key, mask) in events: + if key.data is None: + user = User(key.fileobj) + + users.append(user) + user.run() + + except KeyboardInterrupt: + print("Caught keyboard interrupt, exiting") + + finally: + for user in users: + user.close() + + selector.close() + + init()