diff --git a/src/client/client.py b/src/client/client.py
new file mode 100644
index 0000000..1a2cfab
--- /dev/null
+++ b/src/client/client.py
@@ -0,0 +1,66 @@
+import socket
+import threading
+import sys
+
+#Wait for incoming data from server
+#.decode is used to turn the message in bytes to a string
+def receive(socket, stop_event):
+    while True:
+        if stop_event.is_set():
+            break
+        socket.settimeout(1.0)  # 1 second timeout
+        try:
+            data = b''
+            total_size = 0
+            max_size = 1024 * 1024  # 1MB limit
+            while True:
+                chunk = socket.recv(4096)
+                data += chunk
+                total_size += len(chunk)
+                if total_size > max_size:
+                    print("Message too large, discarding")
+                    break
+                if len(chunk) < 4096:
+                    break
+            if data:
+                print(str(data.decode('utf-8')))
+        except (socket.error, ConnectionResetError, socket.timeout) as e:
+            print(f"You have been disconnected from the server. Error: {str(e)}")
+            stop_event.set()
+            break
+
+#Get host and port
+host = input("Host: ")
+try:
+    port = int(input("Port: "))
+    if not (1024 <= port <= 65535):
+        raise ValueError("Port must be between 1024 and 65535")
+except ValueError as e:
+    print(f"Invalid port: {str(e)}")
+    sys.exit(1)
+
+#Attempt connection to server
+try:
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    sock.connect((host, port))
+except (socket.error, ConnectionRefusedError) as e:
+    print(f"Could not make a connection to the server. Error: {str(e)}")
+    input("Press enter to quit")
+    sys.exit(0)
+
+#Create new thread to wait for data
+stop_event = threading.Event()
+receiveThread = threading.Thread(target = receive, args = (sock, stop_event))
+receiveThread.start()
+
+#Send data to server
+#str.encode is used to turn the string message into bytes so it can be sent across the network
+# Setup clean exit
+try:
+    while True:
+        message = input()
+        sock.sendall(str.encode(message))
+finally:
+    stop_event.set()
+    sock.close()
+    receiveThread.join()
diff --git a/src/server/server.py b/src/server/server.py
new file mode 100644
index 0000000..3d23e5b
--- /dev/null
+++ b/src/server/server.py
@@ -0,0 +1,114 @@
+import socket
+import threading
+
+#Variables for holding information about connections
+connections = []
+total_connections = 0
+_connections_lock = threading.Lock()
+
+#Client class, new instance created for each connected client
+#Each instance has the socket and address that is associated with items
+#Along with an assigned ID and a name chosen by the client
+class Client(threading.Thread):
+    def __init__(self, socket, address, id, name, signal):
+        threading.Thread.__init__(self)
+        self.socket = socket
+        self.address = address
+        self.id = id
+        self.name = name
+        self.signal = signal
+    
+    def __str__(self):
+        return str(self.id) + " " + str(self.address)
+    
+    #Attempt to get data from client
+    #If unable to, assume client has disconnected and remove him from server data
+    #If able to and we get data back, print it in the server and send it back to every
+    #client aside from the client that has sent it
+    #.decode is used to convert the byte data into a printable string
+    def run(self):
+        while self.signal:
+            try:
+                data = b''
+                total_size = 0
+                max_size = 1024 * 1024  # 1MB limit
+                while True:
+                    chunk = self.socket.recv(4096)
+                    data += chunk
+                    total_size += len(chunk)
+                    if total_size > max_size:
+                        print(f"Client {self.id} sent too large message, disconnecting")
+                        self.signal = False
+                        break
+                    if len(chunk) < 4096:
+                        break
+            except (socket.error, ConnectionResetError):
+                print("Client " + str(self.address) + " has disconnected")
+                self.signal = False
+                with _connections_lock:
+                    connections.remove(self)
+                break
+            if data != b"":
+                print("ID " + str(self.id) + ": " + str(data.decode('utf-8')))
+                with _connections_lock:
+                    for client in connections:
+                        if client.id != self.id:
+                            client.socket.sendall(data)
+
+#Wait for new connections
+def newConnections(socket, stop_event):
+    while not stop_event.is_set():
+        sock, address = socket.accept()
+        with _connections_lock:
+            global total_connections
+            connections.append(Client(sock, address, total_connections, "Name", True))
+            connections[len(connections) - 1].start()
+            print("New connection at ID " + str(connections[len(connections) - 1]))
+            total_connections += 1
+
+def main():
+    #Get host and port
+    host = input("Host: ")
+    if not host:
+        host = "localhost"
+    
+    try:
+        port = int(input("Port: "))
+    except ValueError:
+        print("Port must be a number")
+        return
+    if not (1024 <= port <= 65535):
+        raise ValueError("Port must be between 1024 and 65535")
+
+    #Create new server socket
+    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    try:
+        sock.bind((host, port))
+        sock.listen(5)
+    except socket.error as e:
+        print(f"Failed to bind socket: {e.strerror}")
+        return
+
+    stop_event = threading.Event()
+    #Create new thread to wait for connections
+    newConnectionsThread = threading.Thread(target = newConnections, args = (sock, stop_event))
+    newConnectionsThread.start()
+
+    try:
+        stop_event.wait()
+    except KeyboardInterrupt:
+        print("\nShutting down server...")
+        for client in connections[:]:
+            client.signal = False
+            client.socket.close()
+            try:
+                client.join(timeout=1.0)
+            except threading.ThreadError:
+                pass
+        stop_event.set()
+        newConnectionsThread.join()
+        sock.close()
+        print("Server shutdown complete")
+
+if __name__ == "__main__":
+    main()