diff --git a/client.py b/client.py index a609e05aa36949c224addcf55602bb7131db93e6..494c50822ffb3413f0caa7156f6bdcf51ab27525 100644 --- a/client.py +++ b/client.py @@ -1,30 +1,88 @@ import socket import threading +from datetime import datetime -def receive_messages(): - while True: - try: - # Empfange Nachricht vom Server - data = client_socket.recv(1024) - print(data.decode('utf-8')) - except: - break - -# Client-Konfiguration -host = '127.0.0.1' -port = 5555 - -# Verbinde zum Server -client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -client_socket.connect((host, port)) -print(f'Du bist jetzt im Chat') - - -# Starte einen Thread, um Nachrichten vom Server zu empfangen -receive_thread = threading.Thread(target=receive_messages) -receive_thread.start() - -# Haupt-Thread zum Senden von Nachrichten -while True: - message = input() - client_socket.send(message.encode('utf-8')) +MULTICAST_GROUP_IP = '224.1.1.1' + +# Broadcast address and port + +CLIENT_MULTICAST_PORT = 5973 + +# Local host information +MY_HOST = socket.gethostname() +MY_IP = socket.gethostbyname(MY_HOST) + + +class Client(): + def __init__(self): + self.currentLeader = '' + self.server_socket = None + + # print the current date and time + def printwt(self, msg): + current_date_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + print(f'[{current_date_time}] {msg}') + + # dynamic discoverey: client sends request to server group and gets the IP of server as reply + def MulticastSendAndReceive(self): + # Create a UDP socket + multicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + # Set the time-to-live for messages to 1 so they do not go past the local network segment + multicast_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1) + + # Send message on multicast address + message = 'New client wants to connect: ' + MY_IP + multicast_socket.sendto(str.encode(message), (MULTICAST_GROUP_IP, CLIENT_MULTICAST_PORT)) + + self.printwt("Sent my IP to server group") + + while True: + try: + # receive reply data (server IP) from the other participants + reply, addr = multicast_socket.recvfrom(1024) + + if reply: + # decode received data + reply_addr = reply.decode() + self.currentLeader = reply_addr + self.printwt(f'Got Leader address: {self.currentLeader}') + + # Connect to the server using TCP + self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.server_socket.connect((self.currentLeader, 5555)) + print("You have entered the chat room") + + # Starte einen Thread, um Nachrichten zu empfangen + receive_thread = threading.Thread(target=self.receive_messages) + receive_thread.start() + + # Haupt-Thread zum Senden von Nachrichten + while True: + message = input() + timestamped_message = f'[{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] {message}' + self.server_socket.send(timestamped_message.encode('utf-8')) + + except socket.timeout: + pass + + def receive_messages(self): + while True: + try: + data = self.server_socket.recv(1024) + if not data: + break + print(data.decode('utf-8')) + except: + break + + + + +# starting all simultaneously working procedures +if __name__ == "__main__": + client = Client() + + thread1 = threading.Thread(target = client.MulticastSendAndReceive) + thread1.start() + thread1.join() \ No newline at end of file diff --git a/server.py b/server.py index 0183a099b94b3e22dfb00c2075d5861b763de6f1..e5635903724c93c467e57370598fa7d5294fd086 100644 --- a/server.py +++ b/server.py @@ -1,59 +1,219 @@ import socket import threading +from datetime import datetime +import time +import struct -def handle_client(client_socket, client_address): - while True: - try: - # Empfange Nachricht vom Client - data = client_socket.recv(1024) - if not data: +MULTICAST_GROUP_IP = '224.1.1.1' + +# Listening port Server Discovery +SERVER_MULTICAST_PORT = 5974 + +# Listening port Client Discovery +CLIENT_MULTICAST_PORT = 5973 + +# Local host information +MY_HOST = socket.gethostname() +MY_IP = socket.gethostbyname(MY_HOST) + + +class Server(): + def __init__(self): + self.leader_IP = '' # fix the leader IP + self.serverList = [] # list if servers and their addresses + self.clients = [] + self.informServer = False + self.isLeader = False # New variable to track if the server is the leader + + + def printwt(self, msg): + current_date_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + print(f'[{current_date_time}] {msg}') + + + def print_group_view(self): + print("Group view is:", self.serverList) + + + #This function enables the server to listen to the server multicast port and reply the ip address + def MulticastListenAndReply(self): + + # if my IP is not in the server list add it + if MY_IP not in self.serverList: + self.serverList.append(MY_IP) + + # create socket bind to server address + multicast_listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + multicast_listen_sock.bind(('', SERVER_MULTICAST_PORT)) + + # tell the os to add the socket to the multicast group + multicast_group = socket.inet_aton(MULTICAST_GROUP_IP) + mreg = struct.pack('4sL', multicast_group, socket.INADDR_ANY) + multicast_listen_sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreg) + + + while True: + + data, address = multicast_listen_sock.recvfrom(1024) + + if data: + newServer_address = data.decode() + self.printwt(f'New participant wants to connect: {newServer_address}') + self.isLeader = False + + # if the decoded address is not in the server list add it and print the list + + if newServer_address not in self.serverList: + self.serverList.append(newServer_address) + + reply_message = MY_IP + multicast_listen_sock.sendto(str.encode(reply_message), address) + + self.printwt('Replied my IP to new participant') + + + + time.sleep(1) + self.print_group_view() + self.printwt(f'The current leader IP is: {self.leader_IP}') + + + #this function enables the server to send a multicast to the server group and receive the answers of existing members + def MulticastSendAndReceive(self): + + # create socket + multicast_group = (MULTICAST_GROUP_IP, SERVER_MULTICAST_PORT) + multicast_send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + # Set a timeout so the socket does not block indefinitely when trying to receive data. + multicast_send_sock.settimeout(2) + + # Set the time-to-live for messages to 1 so they do not go past the local network segment. + multicast_send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1) + + message = MY_IP + multicast_send_sock.sendto(message.encode(), multicast_group) + self.printwt("Sent my IP to server group") + + # if my IP is not in the server list add it + if MY_IP not in self.serverList: + self.serverList.append(MY_IP) + + # listen for IPs from existing servers + maxLoop = 5 + currentLoop = 0 + + # Anzahl der eingehenden Antworten initialisieren + num_responses = 0 + + while currentLoop < maxLoop: + print("Waiting for responses...") # Debug-Ausgabe + while True: + currentLoop += 1 + + try: + # receive reply data from the other participants + reply, address = multicast_send_sock.recvfrom(1024) + + if reply: + reply_address = reply.decode() + + # Debug-Ausgabe + print(f"Received response from: {reply_address}") + + # if reply address is not in the server list, add it + if reply_address not in self.serverList: + self.serverList.append(reply_address) + + # Erhöhe die Anzahl der eingehenden Antworten + num_responses += 1 + print(f"Current server list: {self.serverList}") # Debug-Ausgabe + + except socket.timeout: + break + + if num_responses == 1: + multicast_send_sock.close() + self.isLeader = True + self.leader_IP = MY_IP # Hier wird die IP-Adresse des Leaders zugewiesen + self.printwt(f'I am the only server in the system, so the leader IP is: {self.leader_IP}') + time.sleep(1) + + + self.print_group_view() + + + # Listen to client multicast (request) and reply with Server IP + def ListenForClientAndReply(self): + # Create a UDP socket + listen_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + # Enable reusing addresses + listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + # Bind socket to address and port + listen_socket.bind((MY_IP, CLIENT_MULTICAST_PORT)) + + # tell the os to add the socket to the multicast group + multicast_group = socket.inet_aton(MULTICAST_GROUP_IP) + mreg = struct.pack('4sL', multicast_group, socket.INADDR_ANY) + listen_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreg) + + # print("Listening to multicast messages") + + # Receiving multicast massage + while True: + data, addr = listen_socket.recvfrom(1024) + + if data: + self.printwt(data.decode()) + + # if Iam the leader, answer the client including my IP + if MY_IP == self.leader_IP: + reply_message = MY_IP + listen_socket.sendto(str.encode(reply_message), addr) + self.printwt('Replied my IP to new client') + + def handle_client(self, client_socket, client_address): + self.clients.append(client_socket) + + while True: + try: + data = client_socket.recv(1024) + if not data: + break + self.multicast(data, client_socket) + + except: + self.clients.remove(client_socket) break - # Sende die empfangene Nachricht an alle anderen Clients - broadcast(data, client_socket, client_address) - except: - break - - # Client-Socket schließen - client_socket.close() - -def broadcast(message, sender_socket, sender_address): - for client in clients: - try: - # Sende die Nachricht an alle Clients, außer an den Absender - if client != sender_socket: - client.send(f"{sender_address}: {message}".encode('utf-8')) - except: - # Entferne defekte Verbindungen - clients.remove(client) - -# Server-Konfiguration -host = '192.168.2.42' -port = 5555 - -# Socket erstellen und binden -server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -server_socket.bind((host, port)) -server_socket.listen(5) - -print(f'Server lauscht auf {host}:{port}') - -# Liste für die verbundenen Clients -clients = [] -servers = [server_socket] - -while True: - # Warten auf eine Verbindung - connection_socket, client_address = server_socket.accept() - - # Wenn eine Verbindung von einem anderen Server kommt, füge ihn zur Serverliste hinzu - if connection_socket != server_socket: - servers.append(connection_socket) - else: - # Neuen Thread für jeden verbundenen Client erstellen - client_thread = threading.Thread(target=handle_client, args=(connection_socket, client_address)) - client_thread.start() - - # Client-Socket zur Liste hinzufügen - clients.append(connection_socket) + def multicast(self, message, sender_socket): + for client in self.clients: + try: + if client != sender_socket: + client.send(message) + except: + self.clients.remove(client) + + +# starting all simultaneously working procedures +if __name__== '__main__': + server = Server() + + thread2 = threading.Thread(target = server.MulticastListenAndReply) + thread2.start() + + thread3 = threading.Thread(target = server.MulticastSendAndReceive) + thread3.start() + + thread1 = threading.Thread(target = server.ListenForClientAndReply) + thread1.start() + + # Socket erstellen und binden + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.bind((MY_IP, 5555)) + server_socket.listen(5) + + while True: + client_socket, client_address = server_socket.accept() + client_thread = threading.Thread(target=server.handle_client, args=(client_socket, client_address)) + client_thread.start() \ No newline at end of file