diff --git a/2024-11-26_Forming_a_Ring_ohne_Update_Ring-Threading_.py b/2024-11-26_Forming_a_Ring_ohne_Update_Ring-Threading_.py deleted file mode 100644 index 129b5aded0d5a47788432be94362941eeddb26cc..0000000000000000000000000000000000000000 --- a/2024-11-26_Forming_a_Ring_ohne_Update_Ring-Threading_.py +++ /dev/null @@ -1,162 +0,0 @@ -#Ring testebn -#Forming a Ring - -import time -import threading -from uuid import uuid4 -import socket -import uuid -import json -#Neu -leader = False -#Definition Atribute -global members -members = [] #List for members in the ring -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -port = 12348 #Port that is used for the discovery of new server participants -server_ip = "0.0.0.0" #IP that enables the server to receive all messages that are passed to the port -my_ip = str(uuid.uuid4()) #Creating a unique ip Adress using uuid4 -ringport = 12343 - -############################################################################################## -###############Threadig einfügen############################################################## -#Loo until Leader= Falsch # Der neue Leader übernimmt diese Aufgabe - -########################In Bearbeitung#################- Nur hier wird die Globale Neughbour Angesprochen und Geupdated - Achtung Falsch muss um den Eing geleitet werden -def sende_update_ring(): - print("Ich Update den Ring") - Nachricht = json.dumps(members).encode() # Nachricht beinhaltet die Liste mit den Members - # Erstellen eines UDP-Sockets - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Setze den Socket in den Broadcast-Modus - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Sende die Nachricht an die Broadcast-Adresse - sock.sendto(Nachricht, (broadcast_ip, ringport)) - -#Muss dauerhaft erfolgen -def lausche_update_Ring(): - global members - # Erstellen eines UDP-Sockets - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Setze den Socket in den Broadcast-Modus - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Sende die Nachricht an die Broadcast-Adresse - sock.bind(("0.0.0.0", ringport)) - while True: - data, addr = sock.recvfrom(1024) # Antwort empfangen - try: - members = json.loads(data.decode()) - print(f"Ring Update erhalten: {members}") - #nachricht_update_ring() - except json.JSONDecodeError: - print("Fehler beim Decodieren der JSON-Daten.") - - -########################################################################### -########################################################################### - -#Function of Leader. Server is listeng to the Port to greet new servers and Update Ring Topology -def new_server_in_ring(): - global members - # Erstellen eines UDP-Sockets - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Setze den Socket in den Broadcast-Modus - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Binde den Socket an die Adresse und den Port - sock.bind(("0.0.0.0", port)) - print("Server läuft und wartet auf Broadcast-Nachrichten von neuen Servern.") - while True: - #Lauscht ob es ene Nachricht von anderen servern erhält - data, addr = sock.recvfrom(1024) # Puffergröße 1024 Bytes - print(f"Nachricht empfangen von {addr}: {data.decode()}") - #response = b"Antwort vom Server: Nachricht erhalten!" - # Sende die Antwort zurück an den neuen Server - #sock.sendto(response, addr) - #print(f"Antwort gesendet an {addr}") - new_server_ip, new_server_port = addr - new_ID = data.decode() - new_ID = new_ID.split(": ")[1] - #Prüfen ob die ID bereis vorhanden ist und Server nur kurz die Verbindung verloren hat. Kann eigemtlich nich eintreten, da ID immer neu generiert wird - if new_ID in members: - #msg = f"Welcome Back. Aktueller Ring: {json.dumps(members)}".encode() - Nachricht = json.dumps(members).encode() - else: - members.append(new_ID) - Nachricht = json.dumps(members).encode() - #msg = f"Updated Ring: {json.dumps(members)}".encode() - #Alle anderen im Ring auch noch benachrichtigen.... mit einer Funktion Update Ring() - #Neuneu neu - #sock.sendto(msg, addr) - - ###einfügern - #message = json.dumps(members) - ###Testen ob es beim zweiten mal auch Funktioniert - print(f"Members des Rings sind: {members}") - sock.sendto(Nachricht, addr) - #Naxchricht noch an den anderen server senden! - - ####################################################### - ####################################################### - #sende_update_ring() - - - #Wie kann ich MSG versenden ohne das die zerteilung der nachricht Falsch ist? - -#Server tritt bei und macht sich für andere Server bemerkbar -def server_enters(): - global members - msg = f"Ich bin neu: {my_ip}".encode() #Greeting message of the server entering the chat - # Erstellen eines UDP-Sockets - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Setze den Socket in den Broadcast-Modus - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Sende die Nachricht an die Broadcast-Adresse - sock.sendto(msg, (broadcast_ip, port)) - # Warte etwas auf eine Antwort - sock.settimeout(2) - try: - data, addr = sock.recvfrom(1024) # Antwort empfangen - print(f"Antwort von {addr}") - #Liste der Members wird dem Neuzugang gesendet, Es wird geprüft, ob die json Datei ausgelesen werden kann. Json wird genutzt um Liste Vollständig zu übergeben - try: - members = json.loads(data.decode()) - print(f"Der neue Ring ist: {members}") - #nachricht_update_ring() - except json.JSONDecodeError: - print("Fehler beim Decodieren der JSON-Daten.") - - - - #Übergabe der Ring Teilnehmer durch den Server an Alle anderen Teilnehnmer!! - except socket.timeout: - #If no answer is received the server sets itself as leader - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine ID: {my_ip}") - #Leader fügt sich selbst als Teilnehmer in den Ring ein - - members.append(my_ip) - #Leader ruft die Funktion auf, um andere beiretenden Servern zu lauschen - #New New - #Global Leader - #Leader = True - new_server_in_ring() - -#Server tritt in den Ring ein/formt den Ring - - -if __name__ == "__main__": - # Thread 1 und 2 erstellen - thread1 = f"Thread1-{my_ip}" - #thread2 = f"Thread2-{my_ip}" - #print(thread1) - #print(thread2) - - thread1 = threading.Thread(target=server_enters) - thread2 = threading.Thread(target=lausche_update_Ring) - - # Starten der Threads - thread1.start() - #thread2.start() - - -# Schließe den Socket an welchem Punkt?? -#sock.close() diff --git a/2024-12-16_Server.py b/2024-12-16_Server.py deleted file mode 100644 index 82dab6937ac8be40c1beb6f3553d9698c7e599ac..0000000000000000000000000000000000000000 --- a/2024-12-16_Server.py +++ /dev/null @@ -1,317 +0,0 @@ -#Ring testebn -#Forming a Ring - -import time -import threading -from uuid import uuid4 -import socket -import uuid -import json -import neighbour -import multiprocessing -import os -from multiprocessing import Manager -from collections import deque - -broadcast_ip = '255.255.255.255' -broadcast_port = 55555 -election_port= 55559 - - -#dict member = {"uuid": {"IP",}} -#Definition Atribute -global members_UUID -global member_IP -members_UUID = [] #List for members in the ring -member_IP = [] #List for ip Adresses of the members -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -port = 12348 #Port that is used for the discovery of new server participants -server_ip = "0.0.0.0" #IP that enables the server to receive all messages that are passed to the port -myuuid = uuid.uuid4() -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 -ringport = 12343 -election_socket = 12345 -hostname = socket.gethostname() -ip_address = socket.gethostbyname(hostname) ##### Für Tests anpassen "127.0.0.1" -participating = False -is_leader = False -Leader = False -ELECTION = 0 -NEW_LEAD = 1 - -#Muss dauerhaft erfolgen -def lausche_update_Ring(): - global member_IP - - # Erstellen eines UDP-Sockets - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Setze den Socket in den Broadcast-Modus Wöfür Frage MF????? - #sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Sende die Nachricht an die Broadcast-Adresse - sock.bind((ip_address, ringport)) - while True: - try: - data, addr = sock.recvfrom(1024) # Antwort empfangen - member_IP2 = json.loads(data.decode()) - #Abgleich, ob Member_IP2 gleich Member_Ip ist - if member_IP2 == member_IP: - print(f"Ring Update bereits erhalten: {member_IP}") - else: - member_IP = member_IP2 - print(f"Ring Update erhalten: {member_IP}") - send_update_to_ring() - #nachricht_update_ring() - except json.JSONDecodeError: - print("Fehler beim Decodieren der JSON-Daten.") - - -#Function of Leader. Server is listeng to the Port to greet new servers and Update Ring Topology -def new_server_in_ring(): - global members_UUID - global member_IP - - # Erstellen eines UDP-Sockets - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Setze den Socket in den Broadcast-Modus - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Binde den Socket an die Adresse und den Port - sock.bind(("0.0.0.0", port)) - print("Server läuft und wartet auf Broadcast-Nachrichten von neuen Servern.") - while True: - #Lauscht ob es ene Nachricht von anderen servern erhält - data, addr = sock.recvfrom(1024) # Puffergröße 1024 Bytes - print(f"Nachricht empfangen von {addr}: {data.decode()}") - msg = 'Es gibt bereits server' - new_server_ip, new_server_port = addr - new_IP = data.decode() - new_IP = new_IP.split(": ")[1] - - # ??? notwendig ??? - #Prüfen ob die ID bereis vorhanden ist und Server nur kurz die Verbindung verloren hat. Kann eigemtlich nich eintreten, da ID immer neu generiert wird - if new_IP in member_IP: - #msg = f"Welcome Back. Aktueller Ring: {json.dumps(members)}".encode() - Nachricht = json.dumps(member_IP).encode() - else: - #members_UUID.append(new_ID) ####UUID wird in Nachricht nicht mehr übergeben - member_IP.append(new_IP) - sock.sendto(msg.encode(), (new_IP, new_server_port)) - #print(f"Members des Rings sind: {members_UUID}") - print(f"Der neue IP_Ring ist: {member_IP}") - - #Ring Update losschicken - send_update_to_ring() - - - -#Server tritt bei und macht sich für andere Server bemerkbar -def server_enters(): - global members_UUID - msg = f"Ich bin neu: {ip_address}".encode() #Greeting message of the server entering the chat - # Erstellen eines UDP-Sockets - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Setze den Socket in den Broadcast-Modus - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - # Sende die Nachricht an die Broadcast-Adresse - sock.sendto(msg, (broadcast_ip, port)) - # Warte etwas auf eine Antwort - sock.settimeout(2) - try: - data, addr = sock.recvfrom(1024) # Antwort empfangen - print(f"Antwort von {addr}: {data.decode()}") - #lausche_update_Ring() Nicht Notwendig, da Funktion dauerhaft im Hintergrund erfolgt.... - sock.close() - #Liste der Members wird dem Neuzugang gesendet, Es wird geprüft, ob die json Datei ausgelesen werden kann. Json wird genutzt um Liste Vollständig zu übergeben - #try: - #members_UUID = json.loads(data.decode()) - #print(f"Der neue Ring ist: {members_UUID}") - #print(f"Der neue IP_Ring ist: {member_IP}") - - #nachricht_update_ring() - #except json.JSONDecodeError: - #print("Fehler beim Decodieren der JSON-Daten.") - - #Übergabe der Ring Teilnehmer durch den Server an Alle anderen Teilnehnmer!! - except socket.timeout: - #If no answer is received the server sets itself as leader - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") - #Leader fügt sich selbst als Teilnehmer in den Ring ein - members_UUID.append(my_ID) - member_IP.append(ip_address) - sock.close() - is_leader.set_value(True) - new_server_in_ring() - -#Server tritt in den Ring ein/formt den Ring -def send_update_to_ring(): - global member_IP - right_neighbour = neighbour.get_neighbour(member_IP, ip_address, 'right') - if not right_neighbour: - print("No left neighbour to send updates.") - return - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - data = json.dumps(member_IP).encode() - try: - sock.sendto(data, (right_neighbour , ringport)) - except Exception as e: - print(f"Error sending data: {e}") - sock.close() - - -############################################################## -############################################################## -############################################################## - - - - -def start_election(): - right_neighbour = neighbour.get_neighbour(member_IP, ip_address, 'right') - print("{} is starting an election.".format(myuuid)) - #Server nimmt an Election Teil - participating = True - #Nachricht wird an den Server neben an gesendet - msg = f"{ELECTION}: {myuuid}".encode('utf-8') - #Msg encoden? Variable setzen? - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_socket)) ########Problem an dieser Stelle?? - #data,addr=send_socket.recvfrom(4096) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - - -def accept(group,erhaltene_uuid): - right_neighbour = neighbour.get_neighbour(member_IP, ip_address, 'right') - if group == ELECTION: - #Abgleich der erhaltenen Uuid mit der eigenen uuid - if erhaltene_uuid > myuuid: - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}".encode() - #Weitergabe der höheren uuid - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_socket)) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - if erhaltene_uuid < myuuid: - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}".encode() - #Weitergabe der höheren uuid - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_socket)) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - if erhaltene_uuid == myuuid: - print("{} starts acting as a leader!".format(myuuid)) - participating = False - #is_leader = True - is_leader.set_value(True) - leader = myuuid - msg = f"{NEW_LEAD}: {myuuid}".encode() - #Weitergabe der höheren uuid - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_socket)) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - if group == NEW_LEAD: - if erhaltene_uuid == myuuid: - return - if erhaltene_uuid != myuuid: - print("{} acknowledged new leader.".format(myuuid)) - is_leader.set_value(False) - leader = myuuid - msg = f"{NEW_LEAD}: {erhaltene_uuid}".encode() - #Weitergabe der höheren uuid - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_socket)) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - - -#zuhören - -#start_election() -def zuhören_election(): - sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - sock.bind((ip_address,election_socket)) - while True: - data,addr=sock.recvfrom(4096) - übernahme = data.decode('utf-8') - #message = ("Hello i am server").encode('utf-8') - #Für Piggiback Acn nutzen? - #sock.sendto(message,addr) - grouprec = int(übernahme.split(": ")[0]) - #print(übernahme) - #print((übernahme.split(": ")[1])) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - accept(grouprec,erhaltene_uuid2) - - -def frage_benutzer(): - # Den Benutzer fragen, ob er die Funktion ausführen möchte - antwort = input("Möchten Sie die Funktion ausführen? (Ja/Nein): ").strip().lower() - if antwort == 'ja': - start_election() - else: - print("Die Funktion wurde nicht ausgeführt.") - - - -####################################################################### -####################################################################### -####################################################################### - - - -#####Mit dieser Funktion wird beobachtet, ob sich ein Wert verändert -class VariableWatcher: - def __init__(self): - self._value = None # Initialisiert den Wert als None - self.observers = [] # Erstellt eine Liste, um die Beobachter zu speichern - - def set_value(self, new_value): - self._value = new_value # Setzt den neuen Wert - self.notify(new_value) # Benachrichtigt alle Beobachter über den neuen Wert - - def add_observer(self, observer): - self.observers.append(observer) # Fügt einen Beobachter zur Liste hinzu - - def notify(self, new_value): - for observer in self.observers: # Iteriert durch alle Beobachter - observer(new_value) # Ruft die Beobachter mit dem neuen Wert auf - - - -#Wenn sich der Wert des is_leaders verändert wird diese veränderung hier geprüft -def callback(new_value): - print(f"Variable value changed to: {new_value}") # Callback-Funktion, die aufgerufen wird, wenn sich der Wert ändert - if new_value==True: - print("Ich übernehme die Aufgaben des leaders") - #new_server_in_ring() - else: - print("Leider bin ich nicht mehr leader") - - - - - - -if __name__ == "__main__": - # Thread 1 und 2 erstellen - #thread1 = f"Thread1-{my_ID}" - thread1 = threading.Thread(target=server_enters) #Achtung eigentlich nur Leader!! - thread2 = threading.Thread(target=lausche_update_Ring) - thread3 = threading.Thread(target=frage_benutzer) - thread4 = threading.Thread(target=zuhören_election) - - # Starten der Threads - thread1.start() - thread2.start() - thread3.start() - thread4.start() - - is_leader = VariableWatcher() - is_leader.add_observer(callback) # Fügt die Callback-Funktion als Beobachter hinzu - -# Schließe den Socket an welchem Punkt?? -#sock.close() diff --git a/2024-12-22_Client_V4.py b/2024-12-22_Client_V4.py deleted file mode 100644 index 979abc7c5fdafda2d9e56f176a1142cb2caf8f1e..0000000000000000000000000000000000000000 --- a/2024-12-22_Client_V4.py +++ /dev/null @@ -1,79 +0,0 @@ -import socket -import threading -import time -import uuid - -broadcast_ip = '255.255.255.255'#change ip??? #hard coded? -broadcast_port = 55555 -broadcast_port2 = 33333 - -#local host information -MY_HOST = socket.gethostname() -#socket.gethostbyname(socket.gethostname()) #getip -MY_IP = socket.gethostbyname(MY_HOST) -#print(f"host:{MY_HOST} and ip: {MY_IP}") - -# create client-socket for broadcast -client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) -client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) -#client_socket.bind(('', broadcast_port)) # listen on broadcast #socket is bind to ALL available IP addresses - -listener_ready = threading.Event() - -#listen for server? -def listen_server(): - client_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - client_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - client_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - client_socket2.bind(('', broadcast_port2)) # listen on broadcast #socket is bind to ALL available IP addresses - """receives messages from server.""" - #listener_ready.set() #makes sure that listener is ready - while True: - try: - data, address = client_socket2.recvfrom(4096) - decoded_message = data.decode() - #print(f"Received {data.decode()} from {address}") #debug - #ignores broadcast messages with own ip #################please enable it after testing!!!!!!!!!!!!!!!!!!!S - - #does not work - if address[0]==MY_IP: - continue - - #print("this decoded msg", decoded_message) #debug - - except socket.error as e: - print(f"An error occurred: {e}") - break - - -def sender(): - """Ermöglicht dem Benutzer, Nachrichten zu schreiben und zu senden.""" - nickname = input("Enter your nickname: ") - just_nickname= f"{nickname} entered the chat".encode() - #client_socket.sendto(just_nickname, (broadcast_ip, broadcast_port)) - client_socket.sendto(just_nickname, (broadcast_ip, broadcast_port)) - #print("is it leader adresse here", leader_address) - while True: - message_id = str(uuid.uuid4()) - #allows the client to send any message - message = input("") - #print("send message to", broadcast_port) - #checks for whitemarks - if message.strip(): - - full_message = f"{message_id}: {nickname}: {message}".encode() - client_socket.sendto(full_message, (broadcast_ip, broadcast_port)) - #print("message sended to", broadcast_port) - -###############main################################# -if __name__ == "__main__": - # Start listener thread - listen_thread = threading.Thread(target=listen_server) - listen_thread.daemon = True - listen_thread.start() - #listener_ready.wait() - - sender_thread = threading.Thread(target=sender) - sender_thread.start() - diff --git a/2024-12-22_Server_V2.py b/2024-12-22_Server_V2.py deleted file mode 100644 index 9e29aa8e37f467da62061a890d9ca6147d7c225f..0000000000000000000000000000000000000000 --- a/2024-12-22_Server_V2.py +++ /dev/null @@ -1,327 +0,0 @@ - -import time -import threading -from uuid import uuid4 -import socket -import uuid -import json -import neighbour -import multiprocessing -import os -from multiprocessing import Manager -from collections import deque - -# Global variables to manage ring members and their information -global members_UUID -global members_IP -# Initialize lists to keep track of members -members_UUID = [] # List for UUIDs of members in the ring -members_IP = [] # List for IP addresses of members in the ring -# Network and port configurations -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -enter_port = 12348 #Port that is used for the discovery of new server participants -ringport = 12343 -election_port = 12345 -acknowledgement_port = 22222 -# Unique identification for this server -myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 -hostname = socket.gethostname() -ip_address = socket.gethostbyname(hostname) # Retrieves the hostname of the current machine -#ip_address = "127.0.0.2" ########verwenden zum Testen auf einem Gerät -# Leader election-related variables -participating = False # Indicates whether the server is currently participating in an election -is_leader = False # Boolean flag to indicate if the server is the leader -Leader = False # Alternate flag to indicate leader status (can be consolidated with is_leader) -ELECTION = 0 # Message type for initiating an election -NEW_LEAD = 1 # Message type for announcing a new leader -leader_ip = 'unknown' # Stores the IP address of the current leader; default is 'unknown' - -########################### Start - Acknowledgement ############################ -def send_acknowledgement(): - """ - Function for sending an acknowledgment for a received message. - """ - right_neighbour = neighbour.get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - msg=("Your Message was received.") # Message sent as a receipt confirmation - send_ack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket to send the acknowledgment - send_ack_socket.sendto(msg.encode('utf-8'),(right_neighbour,acknowledgement_port)) # Send the acknowledgment message to the right neighbor using the acknowledgement_port - send_ack_socket.close() # Close the socket after sending the message - -def receive_acknowledgement(msg, ip, so): - """ - Function for receiving acknowledgments. This section includes the system's response to messages not received between servers. - """ - global members_IP - global is_leader - right_neighbour = neighbour.get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - recack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to receive acknowledgment messages - recack_socket.bind((ip_address,acknowledgement_port)) - recack_socket.settimeout(1) # Set a timeout for receiving acknowledgment --> Notwendig in welcher Höhe???? Testen?? - try: - data, addr = recack_socket.recvfrom(1024) # Attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: - print(f"No response received. Retrying message delivery.") # If no response is received, resend the message - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Open a temporary socket for resending the original message - temp_send_socket.sendto(msg,(ip,so)) - temp_send_socket.close() - recack_socket.settimeout(1) # Set a new timeout for acknowledgment - try: - data, addr = recack_socket.recvfrom(1024) # Second attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: # If the second attempt fails, handle server failure - print(f"No response again. Server is unreachable. Triggering ring update.") - recack_socket.close() - members_IP.remove(right_neighbour) # Remove the failed server from the member list - # Check if the failed server was the leader - if len(members_IP) == 1: - # If only one server remains, it becomes the leader - if leader_ip != ip_address: # Check if the leader IP is its own; only change is_leader to True if the server was not the leader before - is_leader.set_value(True) - print("I am now the last server in this ring and therefore the leader.") - else: - # Update the ring and forward necessary messages - send_update_to_ring() - if right_neighbour == leader_ip: #check if failed server was the leader - # Start a new election since the leader is down - start_election() - #####################################################################Funktion Leader is Down einfügen##################################### - new_neighbour = neighbour.get_neighbour(members_IP, ip_address, 'right') # Determine the new right neighbor with updated ring - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - temp_send_socket.sendto(msg,(new_neighbour,so)) # Forward the original message to the new neighbor - temp_send_socket.close() - receive_acknowledgement(msg, new_neighbour, so) # Wait for acknowledgment from the new neighbor -########################### End - Acknowledgement ########################### - -########################### Start - Update ring ########################### -def lausche_update_Ring(): - """ - Listens for ring updates via UDP broadcasts and handles updates to the members_IP list. - """ - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for communication - sock.bind((ip_address, ringport)) # Bind the socket to the servers IP address and ring port for listening - while True: - try: - data, addr = sock.recvfrom(1024) # Receive data from the socket - members_IP2 = json.loads(data.decode()) # Decode the received JSON data to update the members list - if members_IP2 == members_IP: # Check if the received members list matches the current members list - print(f"Ring update has traveled through the ring.") - send_acknowledgement() # Send an acknowledgment for the received update - else: - members_IP = members_IP2 # Update the local members list - print(f"Ring update received: {members_IP}") - send_acknowledgement() # Send an acknowledgment for the received update - send_update_to_ring() # Forward the updated member list to the next neighbor - except json.JSONDecodeError: - print("Error decoding the JSON data.") # Handle errors in decoding the JSON data - -def send_update_to_ring(): - """ - Sends the updated members list to the next server in the ring. - """ - global members_IP - right_neighbour = neighbour.get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if not right_neighbour: # If no right neighbor exists, there is no one to send the update to - print("No left neighbour to send updates.") - return - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - data = json.dumps(members_IP).encode() # Serialize the members list into JSON format - try: - sock.sendto(data, (right_neighbour , ringport)) # Send the members list to the right neighbor - receive_acknowledgement(data, right_neighbour , ringport) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data: {e}") - sock.close() # Close the socket to free up resources -########################### End - Update ring ########################### - -########################### Start - Server Enters ########################### -def new_server_in_ring(): - """ - This function is executed by the Leader. It listens for incoming messages from servers attempting to join the network. - The Leader maintains the list of all IP addresses in the ring and updates the topology whenever a new server joins. - Topology Updates: The Leader ensures all servers in the network are aware of the latest ring structure. - """ - global members_UUID - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.bind(("0.0.0.0", enter_port)) # Bind the socket to the address "0.0.0.0" and the enter_port - print("Server is running and waiting for broadcast messages from new servers.") - while True: - data, addr = sock.recvfrom(1024) # Listen for messages from other servers - print(f"Message received from {addr}: {data.decode()}") - new_server_ip, new_server_port = addr # Extract the IP and port of the new server from the received address - new_IP = data.decode() - new_IP = new_IP.split(": ")[1] # Extract the IP address from the message - if new_IP in members_IP: # Check if the IP already exists. This might happen if a server temporarily lost connection. #######Tritt das überhaupt ein? In Gruppe diskutieren - msg = json.dumps(members_IP).encode() # If the server already exists, send the updated member list - else: - members_IP.append(new_IP) # If the server is new, add its IP to the list - msg = f"There are already servers. I am the leader: {ip_address}" # Create a message for the new server - sock.sendto(msg.encode(), (new_IP, new_server_port)) # Send the greeting message back to the new server - print(f"The updated IP_Ring is: {members_IP}") - send_update_to_ring() # Update the ring topology - -def server_enters(): - """ - This function is used when a server wants to join the network. It sends a greeting message to the broadcast address and waits for a response from the Leader. - If no response is received, the server assumes the Leader role and starts managing the ring itself. - Broadcast Communication: This allows new servers to discover the Leader without knowing its specific IP address. - """ - global members_UUID - global leader_ip - msg = f"I am new: {ip_address}".encode() # Greeting message from the new server - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.sendto(msg, (broadcast_ip, enter_port)) # Send the greeting message to the broadcast address using the enter_port - sock.settimeout(1) # Set a timeout to wait for a response --> ggf. anpassen, je nach geschwindigkeit mit Handy Internet - try: - data, addr = sock.recvfrom(1024) # receiving response - print(f"Antwort von {addr}: {data.decode()}") - sock.close() - my_leader = data.decode().split(": ")[1] # Extract the Leader's IP address from the response - leader_ip = my_leader # Set leder_ip to the received IP - except socket.timeout: - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") # If no answer is received the server sets itself as leader - members_UUID.append(my_ID) # Add itself as a participant in the ring - members_IP.append(ip_address) # Add itself as a participant in the ring - sock.close() - is_leader.set_value(True) # Mark itself as the Leader - leader_ip = ip_address # Set leder_ip to own IP - new_server_in_ring() # Start the function to manage new servers in the ring --> Achtung nur für tests! ##################################################################### -########################### End - Server Enters ########################### - -########################### Start - Leader Election ########################### -def start_election(): - """ - Initiates an election by sending the server's UUID and IP address to its right neighbor in the ring. - Marks the server as participating in the election process and waits for acknowledgment. - """ - right_neighbour = neighbour.get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - print("{} is starting an election.".format(myuuid)) - participating = True # Server marks itself as participating in the election - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode('utf-8') # Create the election message with the server's UUID and IP address - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to send the election message - send_socket.sendto(msg,(right_neighbour,election_port)) # Send msg to the right neighbor usinf the election_port - send_socket.close() - receive_acknowledgement(msg, right_neighbour, election_port) # Wait for acknowledgment - -def accept(group,erhaltene_uuid,erhaltene_ip): - """ - Function to handle election messages and determine the next steps. - If the message is part of an election (ELECTION), the server compares UUIDs to either forward, update, or declare itself as the leader. - If the message is a new leader announcement (NEW_LEAD), it updates the local leader information and forwards the message. - Leader Election: Based on comparing UUIDs, the server with the highest UUID becomes the leader. - Acknowledgment Mechanism: Ensures that messages are received and processed reliably. - """ - global leader_ip - global is_leader - right_neighbour = neighbour.get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if group == ELECTION: # If the received message is part of an election - # Compare the received UUID with the server's own UUID - if erhaltene_uuid > myuuid: # Received UUID is greater, so forward the message without changes - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}: {erhaltene_ip}".encode() - if erhaltene_uuid < myuuid: # Received UUID is smaller, update with the server's own UUID and forward - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode() - if erhaltene_uuid == myuuid: # If the server receives its own UUID, it becomes the leader - print("{} starts acting as a leader!".format(myuuid)) - participating = False - if leader_ip != ip_address: # Update leadership status if server was not already the leader bevor the election - is_leader.set_value(True) - leader_ip = ip_address # Set leader_ip to own IP - leader = myuuid #Set leader to own uuid - msg = f"{NEW_LEAD}: {myuuid}: {ip_address}".encode() - if group == NEW_LEAD: # If the received message announces a new leader - if erhaltene_uuid == myuuid: # If the UUID matches, the server has already acknowledged - return - if erhaltene_uuid != myuuid: # Update the leader information and forward the new leader announcement - print("{} acknowledged new leader.".format(myuuid)) - if leader_ip == ip_address: # Check if this server was the Leader bevor the election and set is_leader to False - is_leader.set_value(False) - leader_ip = erhaltene_ip # Update leader_ip - leader = erhaltene_uuid # Update leader - msg = f"{NEW_LEAD}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - -def zuhören_election(): - """ - Listens for incoming election or leader messages on the configured election socket. - Decodes the message, sends an acknowledgment to the sender, and processes the message via the accept() function. - """ - sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - sock.bind((ip_address,election_port)) # Bind to the election socket - while True: - data,addr=sock.recvfrom(4096) # Receive data from other servers - übernahme = data.decode('utf-8') # Decode the received message - grouprec = int(übernahme.split(": ")[0]) # Extract group ID, UUID, and IP address from the message - erhaltene_ip = (übernahme.split(": ")[2]) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - send_acknowledgement() # Send acknowledgment back to the sender - accept(grouprec,erhaltene_uuid2,erhaltene_ip) # Process the election or new leader message -########################### End - Leader Election ########################### - -########################### Start - observation value changes leader ########################### -class VariableWatcher: # A utility class designed to monitor a variable's value and notify registered observers whenever the value changes. - def __init__(self): - self._value = None # Initializes the variable's value as None - self.observers = [] # Creates a list to store observers - def set_value(self, new_value): # Updates the variable and notifies observers. - self._value = new_value # Updates the variable's value - self.notify(new_value) # Notifies all observers about the new value - def add_observer(self, observer): - self.observers.append(observer) # Adds an observer to the list - def notify(self, new_value): - for observer in self.observers: # Iterates through all registered observers - observer(new_value) # Calls each observer with the new value - -def callback(new_value): - """ - This function is triggered when the `is_leader` value changes. - """ - print(f"Variable value changed to: {new_value}") # Prints the updated value - if new_value==True: - print("I am now taking over leader responsibilities.") - #new_server_in_ring() - else: - print("I am no longer the leader.") -########################### End - observation value changes leader ########################### - -########################### Only for Testing ########################## -####################################################################### -####################################################################### -def frage_benutzer(): # A test function that prompts the user to decide whether to execute the election process - antwort = input("Möchten Sie die Funktion ausführen? (Ja/Nein): ").strip().lower() - if antwort == 'ja': - start_election() - else: - print("Die Funktion wurde nicht ausgeführt.") -####################################################################### -####################################################################### -####################################################################### - -if __name__ == "__main__": - # Create threads for different server operations - thread1 = threading.Thread(target=server_enters) # Handles server entry to the ring - thread2 = threading.Thread(target=lausche_update_Ring) # Listens for ring updates - thread3 = threading.Thread(target=frage_benutzer) # Prompts the user for action --> Only for testing! - thread4 = threading.Thread(target=zuhören_election) # Listens for election messages - # Start all threads - thread1.start() - thread2.start() - thread3.start() - thread4.start() - - is_leader = VariableWatcher() # Create an instance of VariableWatcher to observe changes in leader status - is_leader.add_observer(callback) # Add the callback function as an observer for changes in `is_leader` - diff --git a/2024-12-22_Server_V4.py b/2024-12-22_Server_V4.py deleted file mode 100644 index 77b8df432a640b8d7cbb1e8c85cd205fadd3fe82..0000000000000000000000000000000000000000 --- a/2024-12-22_Server_V4.py +++ /dev/null @@ -1,406 +0,0 @@ - -import time -import threading -from uuid import uuid4 -import socket -import uuid -import json -import multiprocessing -import os -from multiprocessing import Manager -from collections import deque - -# Global variables to manage ring members and their information -global members_UUID -global members_IP -# Initialize lists to keep track of members -members_UUID = [] # List for UUIDs of members in the ring -members_IP = [] # List for IP addresses of members in the ring -# Network and port configurations -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -enter_port = 12348 #Port that is used for the discovery of new server participants -ringport = 12343 -election_port = 12345 -client_broadcast_port = 55555 -client_broadcast_port2 = 33333 -acknowledgement_port = 22222 -# Unique identification for this server -myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 -hostname = socket.gethostname() -ip_address = socket.gethostbyname(hostname) # Retrieves the hostname of the current machine -#ip_address = "127.0.0.2" ########verwenden zum Testen auf einem Gerät -# Leader election-related variables -participating = False # Indicates whether the server is currently participating in an election -is_leader = False # Boolean flag to indicate if the server is the leader -Leader = False # Alternate flag to indicate leader status (can be consolidated with is_leader) -ELECTION = 0 # Message type for initiating an election -NEW_LEAD = 1 # Message type for announcing a new leader -leader_ip = 'unknown' # Stores the IP address of the current leader; default is 'unknown' -# Heartbeat related variables -last_three_messages = deque(maxlen=3) # Initialization of message storage (last 3 messages) -# variables for Listen to Client -hold_back_queue = deque() # A double-ended queue (deque) to store messages that need to be temporarily held back before they are processed. -processed_message_ids = set() # A set to track the IDs of messages that have already been processed. This helps avoid duplicate processing. - -########################### Start - Neighbour ########################### -def get_neighbour(members_IP, current_member_ip, direction='left'): - """ - Determines the neighbor of a server in a circular ring topology based on the direction. - """ - current_member_index = members_IP.index(current_member_ip) if current_member_ip in members_IP else -1 # Find the index of the current member in the list. If not found, set to -1. - if current_member_index != -1: # Determine the neighbor to the 'left' - if direction == 'left': # Determine the neighbor to the 'left' (next in the ring) - if current_member_index + 1 == len(members_IP): # If the current member is the last in the list, wrap around to the first - return members_IP[0] # Return the first member in the list - else: - return members_IP[current_member_index + 1] # Return the next member in the list - else: # Determine the neighbor to the 'right' - if current_member_index - 1 < 0: # If the current member is the first in the list, wrap around to the last - return members_IP[len(members_IP) - 1] # Return the last member in the list - else: - return members_IP[current_member_index - 1] # Return the previous member in the list - else: - return None # If the current member IP is not found in the list, return None -########################### End - Neighbour ########################### - -########################### Start - Acknowledgement ############################ -def send_acknowledgement(): - """ - Function for sending an acknowledgment for a received message. - """ - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - msg=("Your Message was received.") # Message sent as a receipt confirmation - send_ack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket to send the acknowledgment - send_ack_socket.sendto(msg.encode('utf-8'),(right_neighbour,acknowledgement_port)) # Send the acknowledgment message to the right neighbor using the acknowledgement_port - send_ack_socket.close() # Close the socket after sending the message - -def receive_acknowledgement(msg, ip, so): - """ - Function for receiving acknowledgments. This section includes the system's response to messages not received between servers. - """ - global members_IP - global is_leader - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - recack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to receive acknowledgment messages - recack_socket.bind((ip_address,acknowledgement_port)) - recack_socket.settimeout(1) # Set a timeout for receiving acknowledgment --> Notwendig in welcher Höhe???? Testen?? - try: - data, addr = recack_socket.recvfrom(1024) # Attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: - print(f"No response received. Retrying message delivery.") # If no response is received, resend the message - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Open a temporary socket for resending the original message - temp_send_socket.sendto(msg,(ip,so)) - temp_send_socket.close() - recack_socket.settimeout(1) # Set a new timeout for acknowledgment - try: - data, addr = recack_socket.recvfrom(1024) # Second attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: # If the second attempt fails, handle server failure - print(f"No response again. Server is unreachable. Triggering ring update.") - recack_socket.close() - members_IP.remove(right_neighbour) # Remove the failed server from the member list - # Check if the failed server was the leader - if len(members_IP) == 1: - # If only one server remains, it becomes the leader - if leader_ip != ip_address: # Check if the leader IP is its own; only change is_leader to True if the server was not the leader before - is_leader.set_value(True) - print("I am now the last server in this ring and therefore the leader.") - else: - # Update the ring and forward necessary messages - send_update_to_ring() - if right_neighbour == leader_ip: #check if failed server was the leader - # Start a new election since the leader is down - start_election() - #####################################################################Funktion Leader is Down einfügen##################################### - new_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the new right neighbor with updated ring - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - temp_send_socket.sendto(msg,(new_neighbour,so)) # Forward the original message to the new neighbor - temp_send_socket.close() - receive_acknowledgement(msg, new_neighbour, so) # Wait for acknowledgment from the new neighbor -########################### End - Acknowledgement ########################### - -########################### Start - Update ring ########################### -def lausche_update_Ring(): - """ - Listens for ring updates via UDP broadcasts and handles updates to the members_IP list. - """ - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for communication - sock.bind((ip_address, ringport)) # Bind the socket to the servers IP address and ring port for listening - while True: - try: - data, addr = sock.recvfrom(1024) # Receive data from the socket - members_IP2 = json.loads(data.decode()) # Decode the received JSON data to update the members list - if members_IP2 == members_IP: # Check if the received members list matches the current members list - print(f"Ring update has traveled through the ring.") - send_acknowledgement() # Send an acknowledgment for the received update - else: - members_IP = members_IP2 # Update the local members list - print(f"Ring update received: {members_IP}") - send_acknowledgement() # Send an acknowledgment for the received update - send_update_to_ring() # Forward the updated member list to the next neighbor - except json.JSONDecodeError: - print("Error decoding the JSON data.") # Handle errors in decoding the JSON data - -def send_update_to_ring(): - """ - Sends the updated members list to the next server in the ring. - """ - global members_IP - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if not right_neighbour: # If no right neighbor exists, there is no one to send the update to - print("No left neighbour to send updates.") - return - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - data = json.dumps(members_IP).encode() # Serialize the members list into JSON format - try: - sock.sendto(data, (right_neighbour , ringport)) # Send the members list to the right neighbor - receive_acknowledgement(data, right_neighbour , ringport) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data: {e}") - sock.close() # Close the socket to free up resources -########################### End - Update ring ########################### - -########################### Start - Server Enters ########################### -def new_server_in_ring(): - """ - This function is executed by the Leader. It listens for incoming messages from servers attempting to join the network. - The Leader maintains the list of all IP addresses in the ring and updates the topology whenever a new server joins. - Topology Updates: The Leader ensures all servers in the network are aware of the latest ring structure. - """ - global members_UUID - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.bind(("0.0.0.0", enter_port)) # Bind the socket to the address "0.0.0.0" and the enter_port - print("Server is running and waiting for broadcast messages from new servers.") - while True: - data, addr = sock.recvfrom(1024) # Listen for messages from other servers - print(f"Message received from {addr}: {data.decode()}") - new_server_ip, new_server_port = addr # Extract the IP and port of the new server from the received address - new_IP = data.decode() - new_IP = new_IP.split(": ")[1] # Extract the IP address from the message - if new_IP in members_IP: # Check if the IP already exists. This might happen if a server temporarily lost connection. #######Tritt das überhaupt ein? In Gruppe diskutieren - msg = json.dumps(members_IP).encode() # If the server already exists, send the updated member list - else: - members_IP.append(new_IP) # If the server is new, add its IP to the list - msg = f"There are already servers. I am the leader: {ip_address}" # Create a message for the new server - sock.sendto(msg.encode(), (new_IP, new_server_port)) # Send the greeting message back to the new server - print(f"The updated IP_Ring is: {members_IP}") - send_update_to_ring() # Update the ring topology - -def server_enters(): - """ - This function is used when a server wants to join the network. It sends a greeting message to the broadcast address and waits for a response from the Leader. - If no response is received, the server assumes the Leader role and starts managing the ring itself. - Broadcast Communication: This allows new servers to discover the Leader without knowing its specific IP address. - """ - global members_UUID - global leader_ip - msg = f"I am new: {ip_address}".encode() # Greeting message from the new server - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.sendto(msg, (broadcast_ip, enter_port)) # Send the greeting message to the broadcast address using the enter_port - sock.settimeout(1) # Set a timeout to wait for a response --> ggf. anpassen, je nach geschwindigkeit mit Handy Internet - try: - data, addr = sock.recvfrom(1024) # receiving response - print(f"Antwort von {addr}: {data.decode()}") - sock.close() - my_leader = data.decode().split(": ")[1] # Extract the Leader's IP address from the response - leader_ip = my_leader # Set leder_ip to the received IP - except socket.timeout: - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") # If no answer is received the server sets itself as leader - members_UUID.append(my_ID) # Add itself as a participant in the ring - members_IP.append(ip_address) # Add itself as a participant in the ring - sock.close() - is_leader.set_value(True) # Mark itself as the Leader - leader_ip = ip_address # Set leder_ip to own IP - #new_server_in_ring() # Start the function to manage new servers in the ring --> Achtung nur für tests! ##################################################################### -########################### End - Server Enters ########################### - -########################### Start - Leader Election ########################### -def start_election(): - """ - Initiates an election by sending the server's UUID and IP address to its right neighbor in the ring. - Marks the server as participating in the election process and waits for acknowledgment. - """ - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - print("{} is starting an election.".format(myuuid)) - participating = True # Server marks itself as participating in the election - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode('utf-8') # Create the election message with the server's UUID and IP address - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to send the election message - send_socket.sendto(msg,(right_neighbour,election_port)) # Send msg to the right neighbor usinf the election_port - send_socket.close() - receive_acknowledgement(msg, right_neighbour, election_port) # Wait for acknowledgment - -def accept(group,erhaltene_uuid,erhaltene_ip): - """ - Function to handle election messages and determine the next steps. - If the message is part of an election (ELECTION), the server compares UUIDs to either forward, update, or declare itself as the leader. - If the message is a new leader announcement (NEW_LEAD), it updates the local leader information and forwards the message. - Leader Election: Based on comparing UUIDs, the server with the highest UUID becomes the leader. - Acknowledgment Mechanism: Ensures that messages are received and processed reliably. - """ - global leader_ip - global is_leader - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if group == ELECTION: # If the received message is part of an election - # Compare the received UUID with the server's own UUID - if erhaltene_uuid > myuuid: # Received UUID is greater, so forward the message without changes - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}: {erhaltene_ip}".encode() - if erhaltene_uuid < myuuid: # Received UUID is smaller, update with the server's own UUID and forward - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode() - if erhaltene_uuid == myuuid: # If the server receives its own UUID, it becomes the leader - print("{} starts acting as a leader!".format(myuuid)) - participating = False - if leader_ip != ip_address: # Update leadership status if server was not already the leader bevor the election - is_leader.set_value(True) - leader_ip = ip_address # Set leader_ip to own IP - leader = myuuid #Set leader to own uuid - msg = f"{NEW_LEAD}: {myuuid}: {ip_address}".encode() - if group == NEW_LEAD: # If the received message announces a new leader - if erhaltene_uuid == myuuid: # If the UUID matches, the server has already acknowledged - return - if erhaltene_uuid != myuuid: # Update the leader information and forward the new leader announcement - print("{} acknowledged new leader.".format(myuuid)) - if leader_ip == ip_address: # Check if this server was the Leader bevor the election and set is_leader to False - is_leader.set_value(False) - leader_ip = erhaltene_ip # Update leader_ip - leader = erhaltene_uuid # Update leader - msg = f"{NEW_LEAD}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - -def zuhören_election(): - """ - Listens for incoming election or leader messages on the configured election socket. - Decodes the message, sends an acknowledgment to the sender, and processes the message via the accept() function. - """ - sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - sock.bind((ip_address,election_port)) # Bind to the election socket - while True: - data,addr=sock.recvfrom(4096) # Receive data from other servers - übernahme = data.decode('utf-8') # Decode the received message - grouprec = int(übernahme.split(": ")[0]) # Extract group ID, UUID, and IP address from the message - erhaltene_ip = (übernahme.split(": ")[2]) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - send_acknowledgement() # Send acknowledgment back to the sender - accept(grouprec,erhaltene_uuid2,erhaltene_ip) # Process the election or new leader message -########################### End - Leader Election ########################### - -########################### Start - Process client messages ########################### -def process_hold_back_queue(): - """ - Processes messages in the hold-back queue in the correct order. - """ - while hold_back_queue: - message = hold_back_queue.popleft() # Remove the oldest message from the queue - message_id, decoded_message = message - - if message_id not in processed_message_ids: - # Process or forward the message - print(f"Processing message: {decoded_message}") - broadcast(decoded_message) # Forward the message to all participants - processed_message_ids.add(message_id) # Mark the message as processed - -def broadcast(message): - """ - Sends a message to all participants in the network via broadcast. - """ - server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - full_message = f"{message}".encode() # Encode the message - server_socket2.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - -def listen_client(): - """ - Listens for messages from clients, processes them, and broadcasts them to other participants. - """ - server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_socket.bind(('', client_broadcast_port)) # Bind to the broadcast port - while True: # Wait to receive a message from a client - try: - message, client_address = server_socket.recvfrom(4096) # Receive a message from a client - decoded_message = message.decode() # Decode the message - last_three_messages.append(decoded_message) # Store the message for Leader heartbeat - #print(f"Message stored: {last_three_messages}") - message_id = decoded_message.split(":")[0] # Extract the unique message ID (UUID) from the decoded message - if message_id in processed_message_ids: # Check if the message has already been processed - continue # Skip if the message was already processed - hold_back_queue.append((message_id, decoded_message)) # Add the message to the hold-back queue - process_hold_back_queue() # Process messages in the hold-back queue - except socket.error as e: # Handle socket errors - print(f"An error occurred: {e}") - break - except KeyboardInterrupt: # Handle server shutdown via keyboard interrupt ????? Funktioniert das?? - print("\nShutting down server...") - break -########################### End - Process client messages ########################### - -########################### Start - observation value changes leader ########################### -class VariableWatcher: # A utility class designed to monitor a variable's value and notify registered observers whenever the value changes. - def __init__(self): - self._value = None # Initializes the variable's value as None - self.observers = [] # Creates a list to store observers - def set_value(self, new_value): # Updates the variable and notifies observers. - self._value = new_value # Updates the variable's value - self.notify(new_value) # Notifies all observers about the new value - def add_observer(self, observer): - self.observers.append(observer) # Adds an observer to the list - def notify(self, new_value): - for observer in self.observers: # Iterates through all registered observers - observer(new_value) # Calls each observer with the new value - -def callback(new_value): - """ - This function is triggered when the `is_leader` value changes. - """ - if new_value: - print("I am now taking over leader responsibilities.") - thread5 = threading.Thread(target=listen_client) # Listens to client messages - thread6 = threading.Thread(target=new_server_in_ring) # Listens for election messages - thread5.start() - thread6.start() - else: - print("I am no longer the leader.") -########################### End - observation value changes leader ########################### - -########################### Only for Testing ########################## -####################################################################### -####################################################################### -def frage_benutzer(): # A test function that prompts the user to decide whether to execute the election process - antwort = input("Möchten Sie die Funktion ausführen? (Ja/Nein): ").strip().lower() - if antwort == 'ja': - start_election() - else: - print("Die Funktion wurde nicht ausgeführt.") -####################################################################### -####################################################################### -####################################################################### - -if __name__ == "__main__": - # Create threads for different server operations - thread1 = threading.Thread(target=server_enters) # Handles server entry to the ring - thread2 = threading.Thread(target=lausche_update_Ring) # Listens for ring updates - thread3 = threading.Thread(target=frage_benutzer) # Prompts the user for action --> Only for testing! - thread4 = threading.Thread(target=zuhören_election) # Listens for election messages - # Start all threads - thread1.start() - thread2.start() - thread3.start() - thread4.start() - - is_leader = VariableWatcher() # Create an instance of VariableWatcher to observe changes in leader status - is_leader.add_observer(callback) # Add the callback function as an observer for changes in `is_leader` - diff --git a/2024-12-22_Server_mit_Client_Funktion.py b/2024-12-22_Server_mit_Client_Funktion.py deleted file mode 100644 index 3b9bd19af86312ccf89f47e61631defdd6b30dd0..0000000000000000000000000000000000000000 --- a/2024-12-22_Server_mit_Client_Funktion.py +++ /dev/null @@ -1,81 +0,0 @@ - -import socket -import multiprocessing -import uuid -import time -import threading -import os -from multiprocessing import Manager -from collections import deque -myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 - -# Broadcast IP and port configuration -broadcast_ip = '255.255.255.255' -client_broadcast_port = 55555 -ip_address = "127.0.0.1" - -# Socket setup for client broadcast -server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket -server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode -server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allow address reuse -server_socket.bind(('', client_broadcast_port)) # Bind to the broadcast port - -def broadcast(message): - """ - Sends a message to all participants in the network via broadcast. - """ - full_message = f"{message}".encode() # Encode the message - server_socket.sendto(full_message, (broadcast_ip, client_broadcast_port)) # Send the broadcast message - -def listen_client(): - """ - Listens for messages from clients, processes them, and broadcasts them to other participants. - """ - received_message_uuid = set() # Track processed messages using a set of message UUIDs - MAX_MESSAGES = 3 # Limit on the number of messages stored in the queue - message_queue = deque(maxlen=MAX_MESSAGES) # Queue to store recently processed messages - while True: - try: - message, client_address = server_socket.recvfrom(4096) # Receive a message from a client - decoded_message = message.decode() # Decode the message - message_id = decoded_message.split(":")[0] # Extract message UUID - if message_id in received_message_uuid: # check if it's already processed - continue # Skip if the message was already processed - message_queue.append(message_id) # mark message as proccessed - received_message_uuid.add(message_id) - # Remove the oldest message from the set if the queue reaches its limit - if len(message_queue) == MAX_MESSAGES: - removed_id = message_queue.popleft() - received_message_uuid.remove(removed_id) - print("erhaltene msg uuid:", received_message_uuid) - # Ignore messages originating from this server#################################################### Brauchen wir das??? Ich glaube nicht - if decoded_message.startswith(f"{my_ID}"): - continue # if...ignores it - # Check if the message contains the string "entered" - if decoded_message.__contains__("entered"): - print(f"{client_address} entered the chat.") #client_address is the nickname - # Log the received message - print(f"Received from {client_address}: {decoded_message}") - broadcast(decoded_message) # Broadcast the message to other participants - except socket.error as e: # Handle socket errors - print(f"An error occurred: {e}") - break - except KeyboardInterrupt: # Handle server shutdown via keyboard interrupt ????? Funktioniert das?? - print("\nShutting down server...") - break - - -#**************************************Main function************************************************* -if __name__ == "__main__": - multiprocessing.freeze_support() # Ensure multiprocessing works across platforms - print(f"Script started with PID: {os.getpid()}") # Print the process ID of the script Brauchen wir das??????? - print(f"Server is running with IP {ip_address} and broadcasting on port {client_broadcast_port}...") - listener_client_process = multiprocessing.Process(target=listen_client) # Create a process to listen for client messages - try: - listener_client_process.start() # Start the client listener process - except KeyboardInterrupt: - print("\nShutting down server...") - listener_client_process.terminate() # Terminate the listener process - - \ No newline at end of file diff --git a/2025-01-10_Client_V5.py b/2025-01-10_Client_V5.py deleted file mode 100644 index 2d0fc9308b0e44f8c9e1f91497c8d202ab4e256b..0000000000000000000000000000000000000000 --- a/2025-01-10_Client_V5.py +++ /dev/null @@ -1,136 +0,0 @@ -import socket -import threading -import time -import uuid -import sys -import os - -broadcast_ip = '255.255.255.255' #Broadcast-adress in the Network -broadcast_port = 55555 #client sends -broadcast_port2 = 33333 #client listens -heartbeat_client_broadcast_port = 11111 # Defined heartbeat port -#local host information -MY_HOST = socket.gethostname() -MY_IP = socket.gethostbyname(MY_HOST) -#Variables for server heartbeat -is_server_available = True -last_heartbeat = time.time() - -processed_message_ids = set() # A set to track the IDs of messages that have already been processed. This helps avoid duplicate processing. -listener_ready = threading.Event() - -########################### Start - Receiving MSG ########################### -def listen_server(): - """ - Listens for messages broadcasted by the server and processes them. - """ - client_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket to listen for incoming messages - client_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode for the socket - client_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allow the socket to reuse the same address - client_socket2.bind(('', broadcast_port2)) # Bind the socket to the broadcast port and listen on all available IP addresses - """receives messages from server.""" - global message_id # Tracks the ID of the current message - while True: - try: - data, address = client_socket2.recvfrom(4096) # Receive data from the socket - decoded_message = data.decode() # Decode the received message - text = decoded_message - received_uuid, rest_of_text = text.split(":", 1) # Split the message into UUID (unique identifier) and the rest of the text - if received_uuid not in processed_message_ids: # Process the message if it hasn't been processed yet - processed_message_ids.add(received_uuid) # Mark the message as processed - #print(f"Received {data.decode()} from {address}") # Only for Debugging - print(rest_of_text) # Display the message content - #else: ################### Only for debugging - # print("Message ist Doppelt") ################### Only for debugging - except socket.error as e: # Handle and log any errors while listening for messages - print(f"An error occurred while listening: {e}") - continue -########################### End - Receiving MSG ########################### - -########################### Start - Sending MSG ########################### -def sender(): - """ - Allows the user to compose and send messages to the chat. - """ - global message_id # Tracks the unique ID of each message - message_id = str(uuid.uuid4()) # Generate a unique ID for the user's entry message - nickname = input("Enter your nickname: ") # Prompt the user to enter their nickname - just_nickname= f"{message_id}:{nickname} entered the chat".encode() # Create the initial "entered the chat" message - # create client-socket for broadcast - client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) - client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - client_socket.sendto(just_nickname, (broadcast_ip, broadcast_port)) #Send the "entered the chat" message to the broadcast address - processed_message_ids.add(message_id) # Mark the message ID as processed #######+ Können wir als Rückmeldung auch ausgeben lassen - try: - while True: - message = input("") # Allow the user to enter a message - if message.strip(): # Ensure the message is not empty or whitespace - message_id = str(uuid.uuid4()) # Generate a new unique ID for this message - full_message = f"{message_id}:{nickname}: {message}".encode() # Format the message with the user's nickname and content - client_socket.sendto(full_message, (broadcast_ip, broadcast_port)) # Send the message to the broadcast address - processed_message_ids.add(message_id) # Mark the message ID as processed - except KeyboardInterrupt: ######################whelp....................... Funktioniert das??? - # Handle when the user presses Ctrl+C - print(f"\n{nickname} left the chat.") - # Notify others that this client left the chat - message_id = str(uuid.uuid4()) # New message ID - leave_message = f"{nickname} left the chat".encode() - client_socket.sendto(leave_message, (broadcast_ip, broadcast_port)) - time.sleep(2) # Wait briefly before closing the socket - client_socket.close() # Close the socket - sys.exit() # Exit the program -########################### End - Sending MSG ########################### - -########################### Start - heartbeat ########################### -def listen_to_heartbeat(): - """ - Listens for heartbeat messages from the server and updates the timestamp of the last received heartbeat. - """ - global last_heartbeat # Tracks the time of the last received heartbeat - global is_server_available # Indicates whether the server is available - client_heartbeat_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket to listen for incoming heartbeat messages - client_heartbeat_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode for the socket - client_heartbeat_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Allow the socket to reuse the same address - client_heartbeat_socket.bind(('', heartbeat_client_broadcast_port)) # Bind the socket to the broadcast port and listen on all available IP addresses - while True: - try: # Wait for incoming heartbeat messages - data, addr = client_heartbeat_socket.recvfrom(1024) # Receive data from the socket - #print("Server heartbeat received.") # Log that the server's heartbeat was received - Only for De bugging - last_heartbeat = time.time() # Update the timestamp for the last received heartbeat - except Exception as e: # Handle and log any errors during data reception - print(f"Error sending data: {e}") - -def monitor_heartbeat(): - """ - Monitors whether the last heartbeat was received within the timeout period and updates server availability status. - """ - global last_heartbeat # Tracks the time of the last received heartbeat - global is_server_available # Indicates whether the server is available - while True: - time_since_last_heartbeat = time.time() - last_heartbeat # Calculate the time since the last received heartbeat - if time_since_last_heartbeat > 18: # Check if timeout is reached - if is_server_available == True: # If the server was previously available, log that it is now unavailable - print(f"ERROR: Server is Unavailable! Waiting for connection...") - is_server_available = False # Update the server status to unavailable - else: # If the server becomes available again - if is_server_available == False: - is_server_available = True # Update the server status to available - print("Server is up and Running again.") - time.sleep(1) # Check the heartbeat status every second -########################### End - heartbeat ########################### - -###############main################################# -if __name__ == "__main__": - # Start listener thread - listen_thread = threading.Thread(target=listen_server) - listen_thread.daemon = True - listen_thread.start() - #listener_ready.wait() - heartbeat_thread = threading.Thread(target=listen_to_heartbeat) - heartbeat_thread.start() - listen_heartbeat_thread = threading.Thread(target=monitor_heartbeat) - listen_heartbeat_thread.start() - - sender_thread = threading.Thread(target=sender) - sender_thread.start() diff --git a/2025-01-10_Server_V10_mit_Schleife_Eintritt_Server.py b/2025-01-10_Server_V10_mit_Schleife_Eintritt_Server.py deleted file mode 100644 index 6d41da750645d2810b01e18b1584baff5d05702e..0000000000000000000000000000000000000000 --- a/2025-01-10_Server_V10_mit_Schleife_Eintritt_Server.py +++ /dev/null @@ -1,582 +0,0 @@ - -from inspect import _empty -import time -import threading -from uuid import uuid4 -import socket -import uuid -import pickle -import json -import multiprocessing -import os -from multiprocessing import Manager -from collections import deque - -# Global variables to manage ring members and their information -global members_UUID -global members_IP -global last_heartbeat_time -# Initialize lists to keep track of members -members_UUID = [] # List for UUIDs of members in the ring -members_IP = [] # List for IP addresses of members in the ring -# Network and port configurations -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -enter_port = 12348 #Port that is used for the discovery of new server participants -ringport = 12343 -election_port = 12345 -client_broadcast_port = 55555 #client sends, server listens -client_broadcast_port2 = 33333 #server sends, client listens -acknowledgement_port = 22222 -heartbeat_port = 44444 -heartbeat_client_broadcast_port = 11111 -# Unique identification for this server -myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 -hostname = socket.gethostname() -ip_address = socket.gethostbyname(hostname) # Retrieves the hostname of the current machine -#ip_address = "127.0.0.1" ########verwenden zum Testen auf einem Gerät -# Leader election-related variables -participating = False # Indicates whether the server is currently participating in an election -is_leader = False # Boolean flag to indicate if the server is the leader -Leader = False # Alternate flag to indicate leader status (can be consolidated with is_leader) -ELECTION = 0 # Message type for initiating an election -NEW_LEAD = 1 # Message type for announcing a new leader -leader_ip = 'unknown' # Stores the IP address of the current leader; default is 'unknown' -# Heartbeat related variables -last_five_messages = deque(maxlen=5) # Initialization of message storage (last 5 messages) -send_heartbeat = deque() -received_heartbeat = deque() -last_heartbeat_time = time.time() - -# variables for Listen to Client -hold_back_queue = deque() # A double-ended queue (deque) to store messages that need to be temporarily held back before they are processed. -processed_message_ids = set() # A set to track the IDs of messages that have already been processed. This helps avoid duplicate processing. - -########################### Start - Neighbour ########################### -def get_neighbour(members_IP, current_member_ip, direction='left'): - """ - Determines the neighbor of a server in a circular ring topology based on the direction. - """ - current_member_index = members_IP.index(current_member_ip) if current_member_ip in members_IP else -1 # Find the index of the current member in the list. If not found, set to -1. - if current_member_index != -1: # Determine the neighbor to the 'left' - if direction == 'left': # Determine the neighbor to the 'left' (next in the ring) - if current_member_index + 1 == len(members_IP): # If the current member is the last in the list, wrap around to the first - return members_IP[0] # Return the first member in the list - else: - return members_IP[current_member_index + 1] # Return the next member in the list - else: # Determine the neighbor to the 'right' - if current_member_index - 1 < 0: # If the current member is the first in the list, wrap around to the last - return members_IP[len(members_IP) - 1] # Return the last member in the list - else: - return members_IP[current_member_index - 1] # Return the previous member in the list - else: - return None # If the current member IP is not found in the list, return None -########################### End - Neighbour ########################### - -########################### Start - Acknowledgement ############################ -def send_acknowledgement(): - """ - Function for sending an acknowledgment for a received message. - """ - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'left') # Determine the right neighbor based on the current ring structure - msg=("Your Message was received.") # Message sent as a receipt confirmation - send_ack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket to send the acknowledgment - send_ack_socket.sendto(msg.encode('utf-8'),(right_neighbour,acknowledgement_port)) # Send the acknowledgment message to the right neighbor using the acknowledgement_port - send_ack_socket.close() # Close the socket after sending the message - -def receive_acknowledgement(msg, ip, so): - """ - Function for receiving acknowledgments. This section includes the system's response to messages not received between servers. - """ - global members_IP - global is_leader - global leader_ip - - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - recack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to receive acknowledgment messages - recack_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - recack_socket.bind((ip_address,acknowledgement_port)) - recack_socket.settimeout(2) # Set a timeout for receiving acknowledgment --> Notwendig in welcher Höhe???? Testen?? - try: - data, addr = recack_socket.recvfrom(1024) # Attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: - print(f"No response received. Retrying message delivery.") # If no response is received, resend the message - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Open a temporary socket for resending the original message - temp_send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - temp_send_socket.sendto(msg,(ip,so)) - temp_send_socket.close() - recack_socket.settimeout(2) # Set a new timeout for acknowledgment - try: - data, addr = recack_socket.recvfrom(1024) # Second attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: # If the second attempt fails, handle server failure - print(f"No response again. Server is unreachable. Triggering ring update.") - recack_socket.close() - members_IP.remove(right_neighbour) # Remove the failed server from the member list - # Check if the failed server was the leader - if len(members_IP) == 1: - # If only one server remains, it becomes the leader - if leader_ip != ip_address: # Check if the leader IP is its own; only change is_leader to True if the server was not the leader before - leader_ip = ip_address # Set leder_ip to own IP - is_leader.set_value(True) - print("I am now the last server in this ring and therefore the leader.") - else: - # Update the ring and forward necessary messages - send_update_to_ring() - if right_neighbour == leader_ip: #check if failed server was the leader - start_election() - new_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the new right neighbor with updated ring - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - temp_send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - temp_send_socket.sendto(msg,(new_neighbour,so)) # Forward the original message to the new neighbor - temp_send_socket.close() - receive_acknowledgement(msg, new_neighbour, so) # Wait for acknowledgment from the new neighbor -########################### End - Acknowledgement ########################### - -########################### Start - Update ring ########################### -def lausche_update_Ring(): - """ - Listens for ring updates via UDP broadcasts and handles updates to the members_IP list. - """ - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for communication - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock.bind((ip_address, ringport)) # Bind the socket to the servers IP address and ring port for listening - while True: - try: - data, addr = sock.recvfrom(1024) # Receive data from the socket - members_IP2 = json.loads(data.decode()) # Decode the received JSON data to update the members list - if members_IP2 == members_IP: # Check if the received members list matches the current members list - print(f"Ring update has traveled through the ring.") - send_acknowledgement() # Send an acknowledgment for the received update - else: - members_IP = members_IP2 # Update the local members list - print(f"Ring update received: {members_IP}") - send_acknowledgement() # Send an acknowledgment for the received update - send_update_to_ring() # Forward the updated member list to the next neighbor - except json.JSONDecodeError: - print("Error decoding the JSON data.") # Handle errors in decoding the JSON data - -def send_update_to_ring(): - """ - Sends the updated members list to the next server in the ring. - """ - global members_IP - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) > 1:# If no right neighbor exists, there is no one to send the update to - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - data = json.dumps(members_IP).encode() # Serialize the members list into JSON format - sock.sendto(data, (right_neighbour , ringport)) # Send the members list to the right neighbor - sock.close()# Close the socket to free up resources - receive_acknowledgement(data, right_neighbour , ringport) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data to Ring: {e}") -########################### End - Update ring ########################### - -########################### Start - Server Enters ########################### -def new_server_in_ring(): - """ - This function is executed by the Leader. It listens for incoming messages from servers attempting to join the network. - The Leader maintains the list of all IP addresses in the ring and updates the topology whenever a new server joins. - Topology Updates: The Leader ensures all servers in the network are aware of the latest ring structure. - """ - global members_UUID - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.bind(("0.0.0.0", enter_port)) # Bind the socket to the address "0.0.0.0" and the enter_port - print("Server is running and waiting for broadcast messages from new servers.") - while True: - data, addr = sock.recvfrom(1024) # Listen for messages from other servers - print(f"Message received from {addr}: {data.decode()}") - new_server_ip, new_server_port = addr # Extract the IP and port of the new server from the received address - new_IP = data.decode() - new_IP = new_IP.split(": ")[1] # Extract the IP address from the message - if new_IP in members_IP: # Check if the IP already exists. This might happen if a server temporarily lost connection. #######Tritt das überhaupt ein? In Gruppe diskutieren - f"There are already servers. I am your leader: {ip_address}".encode() # Create a message for the new server# If the server already exists, send the Welcome Message - else: - members_IP.append(new_IP) # If the server is new, add its IP to the list - msg = f"There are already servers. I am your leader: {ip_address}".encode() # Create a message for the new server - #AttributeError: 'bytes' object has no attribute 'encode'. Did you mean: 'decode'? - sock.sendto(msg, (new_IP, new_server_port)) # Send the greeting message back to the new server - print(f"The updated IP_Ring is: {members_IP}") - send_update_to_ring() # Update the ring topology - -def server_enters(): - """ - This function is used when a server wants to join the network. It sends a greeting message to the broadcast address and waits for a response from the Leader. - If no response is received, the server assumes the Leader role and starts managing the ring itself. - Broadcast Communication: This allows new servers to discover the Leader without knowing its specific IP address. - """ - global members_UUID - global leader_ip - max_retries = 3 - success = False - intervall = 2 #time to wait fpr a response - msg = f"I am new: {ip_address}".encode() # Greeting message from the new server - - for attempt in range(max_retries): - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.settimeout(intervall) # Set a timeout to wait for a response --> ggf. anpassen, je nach geschwindigkeit mit Handy Internet - print(f"Sending discovery message (attempt{attempt+1}/{max_retries})") - sock.sendto(msg, (broadcast_ip, enter_port)) # Send the greeting message to the broadcast address using the enter_port - try: - while True: - data, addr = sock.recvfrom(1024) # receiving response - print(f"Antwort von {addr}: {data.decode()}") - success = True - #sock.close() - my_leader = data.decode().split(": ")[1] # Extract the Leader's IP address from the response - leader_ip = my_leader # Set leder_ip to the received IP - except socket.timeout: - print(f"No response received for attempt {attempt+1}.") - if success: - print("Response received. Stopping retries.") - - break - if not success: - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") # If no answer is received the server sets itself as leader - members_UUID.append(my_ID) # Add itself as a participant in the ring - members_IP.append(ip_address) # Add itself as a participant in the ring - sock.close() - leader_ip = ip_address # Set leder_ip to own IP - print(leader_ip) - is_leader.set_value(True) # Mark itself as the Leader - sock.close() -########################### End - Server Enters ########################### - -########################### Start - Leader Election ########################### -def start_election(): - """ - Initiates an election by sending the server's UUID and IP address to its right neighbor in the ring. - Marks the server as participating in the election process and waits for acknowledgment. - """ - global participating - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - print("{} is starting an election.".format(myuuid)) - participating = True # Server marks itself as participating in the election - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode('utf-8') # Create the election message with the server's UUID and IP address - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to send the election message - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send msg to the right neighbor usinf the election_port - send_socket.close() - receive_acknowledgement(msg, right_neighbour, election_port) # Wait for acknowledgment - -def accept(group,erhaltene_uuid,erhaltene_ip): - """ - Function to handle election messages and determine the next steps. - If the message is part of an election (ELECTION), the server compares UUIDs to either forward, update, or declare itself as the leader. - If the message is a new leader announcement (NEW_LEAD), it updates the local leader information and forwards the message. - Leader Election: Based on comparing UUIDs, the server with the highest UUID becomes the leader. - Acknowledgment Mechanism: Ensures that messages are received and processed reliably. - """ - global leader_ip - global is_leader - global last_heartbeat_time - global participating - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if group == ELECTION: # If the received message is part of an election - # Compare the received UUID with the server's own UUID - if erhaltene_uuid > myuuid: # Received UUID is greater, so forward the message without changes - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - elif erhaltene_uuid < myuuid and participating==False: # Received UUID is smaller, update with the server's own UUID and forward - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - if erhaltene_uuid == myuuid: # If the server receives its own UUID, it becomes the leader - print("{} starts acting as a leader!".format(myuuid)) - participating = False - leader_ip = ip_address # Set leader_ip to own IP - leader = myuuid #Set leader to own uuid - msg = f"{NEW_LEAD}: {myuuid}: {ip_address}".encode() - #if leader_ip != ip_address: # Update leadership status if server was not already the leader bevor the election - is_leader.set_value(True) - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - - if group == NEW_LEAD: # If the received message announces a new leader - if erhaltene_uuid == myuuid: # If the UUID matches, the server has already acknowledged - return - if erhaltene_uuid != myuuid: # Update the leader information and forward the new leader announcement - print("{} acknowledged new leader.".format(myuuid)) - if leader_ip == ip_address: # Check if this server was the Leader bevor the election and set is_leader to False - is_leader.set_value(False) - leader_ip = erhaltene_ip # Update leader_ip - leader = erhaltene_uuid # Update leader - msg = f"{NEW_LEAD}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - last_heartbeat_time = time.time() - -def zuhören_election(): - """ - Listens for incoming election or leader messages on the configured election socket. - Decodes the message, sends an acknowledgment to the sender, and processes the message via the accept() function. - """ - #sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - #sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - #sock.bind((ip_address,election_port)) # Bind to the election socket - while True: - sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock.bind((ip_address,election_port)) # Bind to the election socket - data,addr=sock.recvfrom(4096) # Receive data from other servers - übernahme = data.decode('utf-8') # Decode the received message - grouprec = int(übernahme.split(": ")[0]) # Extract group ID, UUID, and IP address from the message - erhaltene_ip = (übernahme.split(": ")[2]) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - sock.close() - send_acknowledgement() # Send acknowledgment back to the sender - accept(grouprec,erhaltene_uuid2,erhaltene_ip) # Process the election or new leader message -########################### End - Leader Election ########################### - -########################### Start - Process client messages ########################### -def process_hold_back_queue(): - """ - Processes messages in the hold-back queue in the correct order. - """ - while hold_back_queue: - message = hold_back_queue.popleft() # Remove the oldest message from the queue - message_id, decoded_message = message - if message_id not in processed_message_ids: - # Process or forward the message - print(f"Processing message: {decoded_message}") - broadcast(decoded_message) # Forward the message to all participants - processed_message_ids.add(message_id) # Mark the message as processed - -def broadcast(message): - """ - Sends a message to all participants in the network via broadcast. - """ - server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - full_message = f"{message}".encode() # Encode the message - server_socket2.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - server_socket2.close() - -def listen_client(): - """ - Listens for messages from clients, processes them, and broadcasts them to other participants. - """ - global last_five_messages - server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_socket.bind(('', client_broadcast_port)) # Bind to the broadcast port - while True: # Wait to receive a message from a client - try: - message, client_address = server_socket.recvfrom(4096) # Receive a message from a client - decoded_message = message.decode() # Decode the message - last_five_messages.append(decoded_message) # Store the message for Leader heartbeat - #print(f"Message stored: {last_three_messages}") # Only for debugging - message_id = decoded_message.split(":")[0] # Extract the unique message ID (UUID) from the decoded message - if message_id in processed_message_ids: # Check if the message has already been processed - continue # Skip if the message was already processed - hold_back_queue.append((message_id, decoded_message)) # Add the message to the hold-back queue - process_hold_back_queue() # Process messages in the hold-back queue - except socket.error as e: # Handle socket errors - print(f"An error occurred while listening: {e}") - break - except KeyboardInterrupt: # Handle server shutdown via keyboard interrupt ????? Funktioniert das?? - print("\nShutting down server...") - break -########################### End - Process client messages ########################### - -########################### Start - Leader Heartbeat ################################# -def forward_received_heartbeat_to_clients(): - """ - Forwards the last 5 received heartbeat messages separately to clients via UDP broadcast. - """ - server_socket3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket3.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - print("Starting to send the messages from the heartbeat.") - for message in received_heartbeat: - try: - print(f"Forwarding message: {message}") # Print the message being forwarded for debugging - full_message = f"{message}".encode() # Encode the message - server_socket3.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - except Exception as e: - print(f"Error forwarding message: {e}") - server_socket3.close() # Close the socket to free up resources - -def listen_heartbeat(): - """ - Listens for the leader's heartbeat and forwards it to the right neighbor in the ring. - """ - global received_heartbeat - global last_heartbeat_time - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening - while True: # Receive data from the socket - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening - data, addr = sock.recvfrom(1024) # Receive data from the socket - print("Leader heartbeat received.") - sock.close() - send_acknowledgement() # Send acknowledgment for the received message - received_heartbeat = pickle.loads(data) # Deserialize the received data - print(received_heartbeat) # Debugging. Print the received messages - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - if leader_ip == ip_address: # If the server is the leader - if send_heartbeat==received_heartbeat: - print(f"My heartbeat has traveled through the ring.") - else: - print(f"Received a foreign heartbeat. I am forwarding the messages.") - forward_received_heartbeat_to_clients() - else: # Forward the heartbeat to the right neighbor - print("Heartbeat wird an den nachbarn gesendet") - right_neighbour = get_neighbour(members_IP, ip_address, 'right') - msg = pickle.dumps(received_heartbeat) - try: - sock_heart = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending Hearbeat - sock_heart.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock_heart.sendto(msg, (right_neighbour , heartbeat_port)) # Send heartbeat to the right neighbor - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - sock_heart.close() # Close the socket to free up resources - except Exception as e: # Handle errors during data transmission - print(f"Error sending data lsiten heartbeat: {e}") - - -def leader_send_heartbeat(): - """ - Sends the Heartbeat to the next server in the ring. - """ - global send_heartbeat - global last_heartbeat_time - while True: - send_heartbeat = last_five_messages # Set the send_heartbeat as the last five messages - msg = pickle.dumps(send_heartbeat) # Serialize the messages - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) == 1: # If only this server is part of the Ring it does not have to send an Heartbeat - print("I am the only server. There is no other Server to send a heartbeat to.") - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - else: # Send the heartbeat to the right neighbor - try: - sock_heart_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - sock_heart_send.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock_heart_send.sendto(msg, (right_neighbour , heartbeat_port)) # Send the members list to the right neighbor - sock_heart_send.close() - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data Leader_send_heartbeat: {e}") - sock_heart_send.close() # Close the socket to free up resources - time.sleep(5) # Wait before sending the next heartbeat - -def monitor_heartbeat(): ################ Achtung Testen, ob auch der Leader in ein timeout kommen kann... ggf. vor removal f leader IP einfügen####### - """ - Monitors whether the last heartbeat was received within the timeout period. - """ - global members_IP - global leader_ip - global last_heartbeat_time - while True: - time_since_last_heartbeat = time.time() - last_heartbeat_time # Calculate the time since the last heartbeat - if time_since_last_heartbeat > 18: - print(f"ERROR: No heartbeat received for 18 seconds! Leader is down.") - members_IP.remove(leader_ip) # Remove the Leader server from the member list - if len(members_IP) == 1: # If only one server remains, it becomes the leader - print("I am now the last server in this ring and therefore the leader.") - leader_ip = ip_address # Update the leader IP to the current server - is_leader.set_value(True) - else: - send_update_to_ring() # Update the ring and forward necessary messages - start_election() # Start leader election to determine new Leader - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - time.sleep(1) # Check for heartbeat status every second -########################### End - Leader Heartbeat ############################ - -########################### Start - Client Heartbeat ########################### -def leader_send_Client_heartbeat(): - """ - Broadcasts a heartbeat message to the clients, indicating the server is available. - """ - while True: - msg = ("Server is available.") # Define the heartbeat message - print("I am sending a heartbeat to the client.") # Debugging message indicating that the server is sending a heartbeat - server_client_heartbeat = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_client_heartbeat.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_client_heartbeat.sendto(msg.encode('utf-8'), (broadcast_ip, heartbeat_client_broadcast_port)) # # Send the broadcast message to the specified broadcast IP and port - server_client_heartbeat.close()# Close the socket to free up resources - time.sleep(5) # Wait for 15 seconds before sending the next heartbeat -########################### End - Client Heartbeat ########################### - -########################### Start - observation value changes leader ########################### -class VariableWatcher: # A utility class designed to monitor a variable's value and notify registered observers whenever the value changes. - def __init__(self): - self._value = None # Initializes the variable's value as None - self.observers = [] # Creates a list to store observers - def set_value(self, new_value): # Updates the variable and notifies observers. - self._value = new_value # Updates the variable's value - self.notify(new_value) # Notifies all observers about the new value - def add_observer(self, observer): - self.observers.append(observer) # Adds an observer to the list - def notify(self, new_value): - for observer in self.observers: # Iterates through all registered observers - observer(new_value) # Calls each observer with the new value - -def callback(new_value): - """ - This function is triggered when the `is_leader` value changes. - """ - if new_value: - print("I am now taking over leader responsibilities.") - thread7 = threading.Thread(target=listen_client) # Listens to client messages - thread8 = threading.Thread(target=new_server_in_ring) # Listens for election messages - thread9 = threading.Thread(target= leader_send_Client_heartbeat) # Start sending heartbeats to the client - thread10 = threading.Thread(target= leader_send_heartbeat) - thread7.start() - thread8.start() - thread9.start() - if len(received_heartbeat) > 0: # Check if there are values in received_heartbeat - print("I am sending the last 5 messages of the leader heartbeat to the clients.") - forward_received_heartbeat_to_clients() # Forward the heartbeat to the clients. - thread10.start() # Leader starts sending its heartbeat to its right neighbour. - else: - print("I am no longer the leader.") -########################### End - observation value changes leader ########################### - -####################################################################### - -if __name__ == "__main__": - # Create threads for different server operations - thread1 = threading.Thread(target=server_enters) # Handles server entry to the ring - thread2 = threading.Thread(target=lausche_update_Ring) # Listens for ring updates - thread4 = threading.Thread(target=zuhören_election) # Listens for election messages - thread5 = threading.Thread(target=listen_heartbeat) # Listens for leader heartbeat - thread6 = threading.Thread(target=monitor_heartbeat) # Monitord the leader heartbeat - # Start all threads - thread1.start() - thread2.start() - thread4.start() - thread5.start() - thread6.start() - - is_leader = VariableWatcher() # Create an instance of VariableWatcher to observe changes in leader status - is_leader.add_observer(callback) # Add the callback function as an observer for changes in `is_leader` \ No newline at end of file diff --git a/2025-01-10_Server_V5.py b/2025-01-10_Server_V5.py deleted file mode 100644 index 4692660c8080bdf30e7e7117bf1790d2619a6e8e..0000000000000000000000000000000000000000 --- a/2025-01-10_Server_V5.py +++ /dev/null @@ -1,524 +0,0 @@ - -from inspect import _empty -import time -import threading -from uuid import uuid4 -import socket -import uuid -import pickle -import json -import multiprocessing -import os -from multiprocessing import Manager -from collections import deque - -# Global variables to manage ring members and their information -global members_UUID -global members_IP -# Initialize lists to keep track of members -members_UUID = [] # List for UUIDs of members in the ring -members_IP = [] # List for IP addresses of members in the ring -# Network and port configurations -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -enter_port = 12348 #Port that is used for the discovery of new server participants -ringport = 12343 -election_port = 12345 -client_broadcast_port = 55555 #client sends, server listens -client_broadcast_port2 = 33333 #server sends, client listens -acknowledgement_port = 22222 -heartbeat_port = 44444 -heartbeat_client_broadcast_port = 11111 -# Unique identification for this server -myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 -hostname = socket.gethostname() -#ip_address = socket.gethostbyname(hostname) # Retrieves the hostname of the current machine -ip_address = "127.0.0.1" ########verwenden zum Testen auf einem Gerät -# Leader election-related variables -participating = False # Indicates whether the server is currently participating in an election -is_leader = False # Boolean flag to indicate if the server is the leader -Leader = False # Alternate flag to indicate leader status (can be consolidated with is_leader) -ELECTION = 0 # Message type for initiating an election -NEW_LEAD = 1 # Message type for announcing a new leader -leader_ip = 'unknown' # Stores the IP address of the current leader; default is 'unknown' -# Heartbeat related variables -last_five_messages = deque(maxlen=5) # Initialization of message storage (last 5 messages) -send_heartbeat = deque() -received_heartbeat = deque() -last_heartbeat_time = time.time() - -# variables for Listen to Client -hold_back_queue = deque() # A double-ended queue (deque) to store messages that need to be temporarily held back before they are processed. -processed_message_ids = set() # A set to track the IDs of messages that have already been processed. This helps avoid duplicate processing. - -########################### Start - Neighbour ########################### -def get_neighbour(members_IP, current_member_ip, direction='left'): - """ - Determines the neighbor of a server in a circular ring topology based on the direction. - """ - current_member_index = members_IP.index(current_member_ip) if current_member_ip in members_IP else -1 # Find the index of the current member in the list. If not found, set to -1. - if current_member_index != -1: # Determine the neighbor to the 'left' - if direction == 'left': # Determine the neighbor to the 'left' (next in the ring) - if current_member_index + 1 == len(members_IP): # If the current member is the last in the list, wrap around to the first - return members_IP[0] # Return the first member in the list - else: - return members_IP[current_member_index + 1] # Return the next member in the list - else: # Determine the neighbor to the 'right' - if current_member_index - 1 < 0: # If the current member is the first in the list, wrap around to the last - return members_IP[len(members_IP) - 1] # Return the last member in the list - else: - return members_IP[current_member_index - 1] # Return the previous member in the list - else: - return None # If the current member IP is not found in the list, return None -########################### End - Neighbour ########################### - -########################### Start - Acknowledgement ############################ -def send_acknowledgement(): - """ - Function for sending an acknowledgment for a received message. - """ - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - msg=("Your Message was received.") # Message sent as a receipt confirmation - send_ack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket to send the acknowledgment - send_ack_socket.sendto(msg.encode('utf-8'),(right_neighbour,acknowledgement_port)) # Send the acknowledgment message to the right neighbor using the acknowledgement_port - send_ack_socket.close() # Close the socket after sending the message - -def receive_acknowledgement(msg, ip, so): - """ - Function for receiving acknowledgments. This section includes the system's response to messages not received between servers. - """ - global members_IP - global is_leader - global leader_ip - - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - recack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to receive acknowledgment messages - recack_socket.bind((ip_address,acknowledgement_port)) - recack_socket.settimeout(1) # Set a timeout for receiving acknowledgment --> Notwendig in welcher Höhe???? Testen?? - try: - data, addr = recack_socket.recvfrom(1024) # Attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: - print(f"No response received. Retrying message delivery.") # If no response is received, resend the message - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Open a temporary socket for resending the original message - temp_send_socket.sendto(msg,(ip,so)) - temp_send_socket.close() - recack_socket.settimeout(1) # Set a new timeout for acknowledgment - try: - data, addr = recack_socket.recvfrom(1024) # Second attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: # If the second attempt fails, handle server failure - print(f"No response again. Server is unreachable. Triggering ring update.") - recack_socket.close() - members_IP.remove(right_neighbour) # Remove the failed server from the member list - # Check if the failed server was the leader - if len(members_IP) == 1: - # If only one server remains, it becomes the leader - if leader_ip != ip_address: # Check if the leader IP is its own; only change is_leader to True if the server was not the leader before - leader_ip = ip_address # Set leder_ip to own IP - is_leader.set_value(True) - print("I am now the last server in this ring and therefore the leader.") - else: - # Update the ring and forward necessary messages - send_update_to_ring() - if right_neighbour == leader_ip: #check if failed server was the leader - start_election() - new_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the new right neighbor with updated ring - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - temp_send_socket.sendto(msg,(new_neighbour,so)) # Forward the original message to the new neighbor - temp_send_socket.close() - receive_acknowledgement(msg, new_neighbour, so) # Wait for acknowledgment from the new neighbor -########################### End - Acknowledgement ########################### - -########################### Start - Update ring ########################### -def lausche_update_Ring(): - """ - Listens for ring updates via UDP broadcasts and handles updates to the members_IP list. - """ - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for communication - sock.bind((ip_address, ringport)) # Bind the socket to the servers IP address and ring port for listening - while True: - try: - data, addr = sock.recvfrom(1024) # Receive data from the socket - members_IP2 = json.loads(data.decode()) # Decode the received JSON data to update the members list - if members_IP2 == members_IP: # Check if the received members list matches the current members list - print(f"Ring update has traveled through the ring.") - send_acknowledgement() # Send an acknowledgment for the received update - else: - members_IP = members_IP2 # Update the local members list - print(f"Ring update received: {members_IP}") - send_acknowledgement() # Send an acknowledgment for the received update - send_update_to_ring() # Forward the updated member list to the next neighbor - except json.JSONDecodeError: - print("Error decoding the JSON data.") # Handle errors in decoding the JSON data - -def send_update_to_ring(): - """ - Sends the updated members list to the next server in the ring. - """ - global members_IP - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) > 1:# If no right neighbor exists, there is no one to send the update to - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - data = json.dumps(members_IP).encode() # Serialize the members list into JSON format - try: - sock.sendto(data, (right_neighbour , ringport)) # Send the members list to the right neighbor - receive_acknowledgement(data, right_neighbour , ringport) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data: {e}") - sock.close() # Close the socket to free up resources -########################### End - Update ring ########################### - -########################### Start - Server Enters ########################### -def new_server_in_ring(): - """ - This function is executed by the Leader. It listens for incoming messages from servers attempting to join the network. - The Leader maintains the list of all IP addresses in the ring and updates the topology whenever a new server joins. - Topology Updates: The Leader ensures all servers in the network are aware of the latest ring structure. - """ - global members_UUID - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.bind(("0.0.0.0", enter_port)) # Bind the socket to the address "0.0.0.0" and the enter_port - print("Server is running and waiting for broadcast messages from new servers.") - while True: - data, addr = sock.recvfrom(1024) # Listen for messages from other servers - print(f"Message received from {addr}: {data.decode()}") - new_server_ip, new_server_port = addr # Extract the IP and port of the new server from the received address - new_IP = data.decode() - new_IP = new_IP.split(": ")[1] # Extract the IP address from the message - if new_IP in members_IP: # Check if the IP already exists. This might happen if a server temporarily lost connection. #######Tritt das überhaupt ein? In Gruppe diskutieren - msg = json.dumps(members_IP).encode() # If the server already exists, send the updated member list - else: - members_IP.append(new_IP) # If the server is new, add its IP to the list - msg = f"There are already servers. I am your leader: {ip_address}" # Create a message for the new server - #AttributeError: 'bytes' object has no attribute 'encode'. Did you mean: 'decode'? - sock.sendto(msg.encode(), (new_IP, new_server_port)) # Send the greeting message back to the new server - print(f"The updated IP_Ring is: {members_IP}") - send_update_to_ring() # Update the ring topology - -def server_enters(): - """ - This function is used when a server wants to join the network. It sends a greeting message to the broadcast address and waits for a response from the Leader. - If no response is received, the server assumes the Leader role and starts managing the ring itself. - Broadcast Communication: This allows new servers to discover the Leader without knowing its specific IP address. - """ - global members_UUID - global leader_ip - - msg = f"I am new: {ip_address}".encode() # Greeting message from the new server - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.sendto(msg, (broadcast_ip, enter_port)) # Send the greeting message to the broadcast address using the enter_port - sock.settimeout(1) # Set a timeout to wait for a response --> ggf. anpassen, je nach geschwindigkeit mit Handy Internet - try: - data, addr = sock.recvfrom(1024) # receiving response - print(f"Antwort von {addr}: {data.decode()}") - sock.close() - my_leader = data.decode().split(": ")[1] # Extract the Leader's IP address from the response - leader_ip = my_leader # Set leder_ip to the received IP - except socket.timeout: - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") # If no answer is received the server sets itself as leader - members_UUID.append(my_ID) # Add itself as a participant in the ring - members_IP.append(ip_address) # Add itself as a participant in the ring - sock.close() - leader_ip = ip_address # Set leder_ip to own IP - print(leader_ip) - is_leader.set_value(True) # Mark itself as the Leader -########################### End - Server Enters ########################### - -########################### Start - Leader Election ########################### -def start_election(): - """ - Initiates an election by sending the server's UUID and IP address to its right neighbor in the ring. - Marks the server as participating in the election process and waits for acknowledgment. - """ - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - print("{} is starting an election.".format(myuuid)) - participating = True # Server marks itself as participating in the election - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode('utf-8') # Create the election message with the server's UUID and IP address - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to send the election message - send_socket.sendto(msg,(right_neighbour,election_port)) # Send msg to the right neighbor usinf the election_port - send_socket.close() - receive_acknowledgement(msg, right_neighbour, election_port) # Wait for acknowledgment - -def accept(group,erhaltene_uuid,erhaltene_ip): - """ - Function to handle election messages and determine the next steps. - If the message is part of an election (ELECTION), the server compares UUIDs to either forward, update, or declare itself as the leader. - If the message is a new leader announcement (NEW_LEAD), it updates the local leader information and forwards the message. - Leader Election: Based on comparing UUIDs, the server with the highest UUID becomes the leader. - Acknowledgment Mechanism: Ensures that messages are received and processed reliably. - """ - global leader_ip - global is_leader - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if group == ELECTION: # If the received message is part of an election - # Compare the received UUID with the server's own UUID - if erhaltene_uuid > myuuid: # Received UUID is greater, so forward the message without changes - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}: {erhaltene_ip}".encode() - if erhaltene_uuid < myuuid: # Received UUID is smaller, update with the server's own UUID and forward - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode() - if erhaltene_uuid == myuuid: # If the server receives its own UUID, it becomes the leader - print("{} starts acting as a leader!".format(myuuid)) - participating = False - leader_ip = ip_address # Set leader_ip to own IP - leader = myuuid #Set leader to own uuid - msg = f"{NEW_LEAD}: {myuuid}: {ip_address}".encode() - if leader_ip != ip_address: # Update leadership status if server was not already the leader bevor the election - is_leader.set_value(True) - if group == NEW_LEAD: # If the received message announces a new leader - if erhaltene_uuid == myuuid: # If the UUID matches, the server has already acknowledged - return - if erhaltene_uuid != myuuid: # Update the leader information and forward the new leader announcement - print("{} acknowledged new leader.".format(myuuid)) - if leader_ip == ip_address: # Check if this server was the Leader bevor the election and set is_leader to False - is_leader.set_value(False) - leader_ip = erhaltene_ip # Update leader_ip - leader = erhaltene_uuid # Update leader - msg = f"{NEW_LEAD}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - -def zuhören_election(): - """ - Listens for incoming election or leader messages on the configured election socket. - Decodes the message, sends an acknowledgment to the sender, and processes the message via the accept() function. - """ - sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - sock.bind((ip_address,election_port)) # Bind to the election socket - while True: - data,addr=sock.recvfrom(4096) # Receive data from other servers - übernahme = data.decode('utf-8') # Decode the received message - grouprec = int(übernahme.split(": ")[0]) # Extract group ID, UUID, and IP address from the message - erhaltene_ip = (übernahme.split(": ")[2]) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - send_acknowledgement() # Send acknowledgment back to the sender - accept(grouprec,erhaltene_uuid2,erhaltene_ip) # Process the election or new leader message -########################### End - Leader Election ########################### - -########################### Start - Process client messages ########################### -def process_hold_back_queue(): - """ - Processes messages in the hold-back queue in the correct order. - """ - while hold_back_queue: - message = hold_back_queue.popleft() # Remove the oldest message from the queue - message_id, decoded_message = message - if message_id not in processed_message_ids: - # Process or forward the message - print(f"Processing message: {decoded_message}") - broadcast(decoded_message) # Forward the message to all participants - processed_message_ids.add(message_id) # Mark the message as processed - -def broadcast(message): - """ - Sends a message to all participants in the network via broadcast. - """ - server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - full_message = f"{message}".encode() # Encode the message - server_socket2.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - server_socket2.close() - -def listen_client(): - """ - Listens for messages from clients, processes them, and broadcasts them to other participants. - """ - global last_five_messages - server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_socket.bind(('', client_broadcast_port)) # Bind to the broadcast port - while True: # Wait to receive a message from a client - try: - message, client_address = server_socket.recvfrom(4096) # Receive a message from a client - decoded_message = message.decode() # Decode the message - last_five_messages.append(decoded_message) # Store the message for Leader heartbeat - #print(f"Message stored: {last_three_messages}") # Only for debugging - message_id = decoded_message.split(":")[0] # Extract the unique message ID (UUID) from the decoded message - if message_id in processed_message_ids: # Check if the message has already been processed - continue # Skip if the message was already processed - hold_back_queue.append((message_id, decoded_message)) # Add the message to the hold-back queue - process_hold_back_queue() # Process messages in the hold-back queue - except socket.error as e: # Handle socket errors - print(f"An error occurred while listening: {e}") - break - except KeyboardInterrupt: # Handle server shutdown via keyboard interrupt ????? Funktioniert das?? - print("\nShutting down server...") - break -########################### End - Process client messages ########################### - -########################### Start - Leader Heartbeat ################################# -def forward_received_heartbeat_to_clients(): - """ - Forwards the last 5 received heartbeat messages separately to clients via UDP broadcast. - """ - server_socket3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket3.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - print("Starting to send the messages from the heartbeat.") - for message in received_heartbeat: - try: - print(f"Forwarding message: {message}") # Print the message being forwarded for debugging - full_message = f"{message}".encode() # Encode the message - server_socket3.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - except Exception as e: - print(f"Error forwarding message: {e}") - server_socket3.close() # Close the socket to free up resources - -def listen_heartbeat(): - """ - Listens for the leader's heartbeat and forwards it to the right neighbor in the ring. - """ - global received_heartbeat - global last_heartbeat_time - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats - sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening - while True: # Receive data from the socket - data, addr = sock.recvfrom(1024) # Receive data from the socket - print("Leader heartbeat received.") - send_acknowledgement() # Send acknowledgment for the received message - received_heartbeat = pickle.loads(data) # Deserialize the received data - print(received_heartbeat) # Debugging. Print the received messages - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - if leader_ip == ip_address: # If the server is the leader - if send_heartbeat==received_heartbeat: - print(f"My heartbeat has traveled through the ring.") - else: - print(f"Received a foreign heartbeat. I am forwarding the messages.") - forward_received_heartbeat_to_clients() - else: # Forward the heartbeat to the right neighbor - print("Heartbeat wird an den nachbarn gesendet") - right_neighbour = get_neighbour(members_IP, ip_address, 'right') - msg = pickle.dumps(received_heartbeat) - sock_heart = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending Hearbeat - try: - sock_heart.sendto(msg, (right_neighbour , heartbeat_port)) # Send heartbeat to the right neighbor - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data: {e}") - sock_heart.close() # Close the socket to free up resources - -def leader_send_heartbeat(): - """ - Sends the Heartbeat to the next server in the ring. - """ - global send_heartbeat - global last_heartbeat_time - while True: - send_heartbeat = last_five_messages # Set the send_heartbeat as the last five messages - msg = pickle.dumps(send_heartbeat) # Serialize the messages - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) == 1: # If only this server is part of the Ring it does not have to send an Heartbeat - print("I am the only server. There is no other Server to send a heartbeat to.") - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - else: # Send the heartbeat to the right neighbor - sock_heart_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - try: - sock_heart_send.sendto(msg, (right_neighbour , heartbeat_port)) # Send the members list to the right neighbor - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data: {e}") - sock_heart_send.close() # Close the socket to free up resources - time.sleep(15) # Wait before sending the next heartbeat - -def monitor_heartbeat(): ################ Achtung Testen, ob auch der Leader in ein timeout kommen kann... ggf. vor removal f leader IP einfügen####### - """ - Monitors whether the last heartbeat was received within the timeout period. - """ - global members_IP - global leader_ip - while True: - time_since_last_heartbeat = time.time() - last_heartbeat_time # Calculate the time since the last heartbeat - if time_since_last_heartbeat > 18: - print(f"ERROR: No heartbeat received for 18 seconds! Leader is down.") - members_IP.remove(leader_ip) # Remove the Leader server from the member list - if len(members_IP) == 1: # If only one server remains, it becomes the leader - print("I am now the last server in this ring and therefore the leader.") - leader_ip = ip_address # Update the leader IP to the current server - is_leader.set_value(True) - else: - send_update_to_ring() # Update the ring and forward necessary messages - start_election() # Start leader election to determine new Leader - time.sleep(1) # Check for heartbeat status every second -########################### End - Leader Heartbeat ############################ - -########################### Start - Client Heartbeat ########################### -def leader_send_Client_heartbeat(): - """ - Broadcasts a heartbeat message to the clients, indicating the server is available. - """ - while True: - msg = ("Server is available.") # Define the heartbeat message - print("I am sending a heartbeat to the client.") # Debugging message indicating that the server is sending a heartbeat - server_client_heartbeat = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_client_heartbeat.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_client_heartbeat.sendto(msg.encode('utf-8'), (broadcast_ip, heartbeat_client_broadcast_port)) # # Send the broadcast message to the specified broadcast IP and port - server_client_heartbeat.close()# Close the socket to free up resources - time.sleep(15) # Wait for 15 seconds before sending the next heartbeat -########################### End - Client Heartbeat ########################### - -########################### Start - observation value changes leader ########################### -class VariableWatcher: # A utility class designed to monitor a variable's value and notify registered observers whenever the value changes. - def __init__(self): - self._value = None # Initializes the variable's value as None - self.observers = [] # Creates a list to store observers - def set_value(self, new_value): # Updates the variable and notifies observers. - self._value = new_value # Updates the variable's value - self.notify(new_value) # Notifies all observers about the new value - def add_observer(self, observer): - self.observers.append(observer) # Adds an observer to the list - def notify(self, new_value): - for observer in self.observers: # Iterates through all registered observers - observer(new_value) # Calls each observer with the new value - -def callback(new_value): - """ - This function is triggered when the `is_leader` value changes. - """ - if new_value: - print("I am now taking over leader responsibilities.") - thread7 = threading.Thread(target=listen_client) # Listens to client messages - thread8 = threading.Thread(target=new_server_in_ring) # Listens for election messages - thread9 = threading.Thread(target= leader_send_Client_heartbeat) # Start sending heartbeats to the client - thread10 = threading.Thread(target= leader_send_heartbeat) - thread7.start() - thread8.start() - thread9.start() - if len(received_heartbeat) > 0: # Check if there are values in received_heartbeat - print("I am sending the last 5 messages of the leader heartbeat to the clients.") - forward_received_heartbeat_to_clients() # Forward the heartbeat to the clients. - thread10.start() # Leader starts sending its heartbeat to its right neighbour. - else: - print("I am no longer the leader.") -########################### End - observation value changes leader ########################### - -if __name__ == "__main__": - # Create threads for different server operations - thread1 = threading.Thread(target=server_enters) # Handles server entry to the ring - thread2 = threading.Thread(target=lausche_update_Ring) # Listens for ring updates - #thread3 = threading.Thread(target=frage_benutzer) # Prompts the user for action --> Only for testing! - thread4 = threading.Thread(target=zuhören_election) # Listens for election messages - thread5 = threading.Thread(target=listen_heartbeat) # Listens for leader heartbeat - thread6 = threading.Thread(target=monitor_heartbeat) # Monitord the leader heartbeat - # Start all threads - thread1.start() - thread2.start() - #thread3.start() - thread4.start() - thread5.start() - thread6.start() - - is_leader = VariableWatcher() # Create an instance of VariableWatcher to observe changes in leader status - is_leader.add_observer(callback) # Add the callback function as an observer for changes in `is_leader` \ No newline at end of file diff --git a/2025-01-10_Server_V6.py b/2025-01-10_Server_V6.py deleted file mode 100644 index 6c42c084be0e626f3a8f2e8a2a429a32b064f562..0000000000000000000000000000000000000000 --- a/2025-01-10_Server_V6.py +++ /dev/null @@ -1,527 +0,0 @@ - -from inspect import _empty -import time -import threading -from uuid import uuid4 -import socket -import uuid -import pickle -import json -import multiprocessing -import os -from multiprocessing import Manager -from collections import deque - -# Global variables to manage ring members and their information -global members_UUID -global members_IP -# Initialize lists to keep track of members -members_UUID = [] # List for UUIDs of members in the ring -members_IP = [] # List for IP addresses of members in the ring -# Network and port configurations -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -enter_port = 12348 #Port that is used for the discovery of new server participants -ringport = 12343 -election_port = 12345 -client_broadcast_port = 55555 #client sends, server listens -client_broadcast_port2 = 33333 #server sends, client listens -acknowledgement_port = 22222 -heartbeat_port = 44444 -heartbeat_client_broadcast_port = 11111 -# Unique identification for this server -myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 -hostname = socket.gethostname() -ip_address = socket.gethostbyname(hostname) # Retrieves the hostname of the current machine -#ip_address = "127.0.0.1" ########verwenden zum Testen auf einem Gerät -# Leader election-related variables -participating = False # Indicates whether the server is currently participating in an election -is_leader = False # Boolean flag to indicate if the server is the leader -Leader = False # Alternate flag to indicate leader status (can be consolidated with is_leader) -ELECTION = 0 # Message type for initiating an election -NEW_LEAD = 1 # Message type for announcing a new leader -leader_ip = 'unknown' # Stores the IP address of the current leader; default is 'unknown' -# Heartbeat related variables -last_five_messages = deque(maxlen=5) # Initialization of message storage (last 5 messages) -send_heartbeat = deque() -received_heartbeat = deque() -last_heartbeat_time = time.time() - -# variables for Listen to Client -hold_back_queue = deque() # A double-ended queue (deque) to store messages that need to be temporarily held back before they are processed. -processed_message_ids = set() # A set to track the IDs of messages that have already been processed. This helps avoid duplicate processing. - -########################### Start - Neighbour ########################### -def get_neighbour(members_IP, current_member_ip, direction='left'): - """ - Determines the neighbor of a server in a circular ring topology based on the direction. - """ - current_member_index = members_IP.index(current_member_ip) if current_member_ip in members_IP else -1 # Find the index of the current member in the list. If not found, set to -1. - if current_member_index != -1: # Determine the neighbor to the 'left' - if direction == 'left': # Determine the neighbor to the 'left' (next in the ring) - if current_member_index + 1 == len(members_IP): # If the current member is the last in the list, wrap around to the first - return members_IP[0] # Return the first member in the list - else: - return members_IP[current_member_index + 1] # Return the next member in the list - else: # Determine the neighbor to the 'right' - if current_member_index - 1 < 0: # If the current member is the first in the list, wrap around to the last - return members_IP[len(members_IP) - 1] # Return the last member in the list - else: - return members_IP[current_member_index - 1] # Return the previous member in the list - else: - return None # If the current member IP is not found in the list, return None -########################### End - Neighbour ########################### - -########################### Start - Acknowledgement ############################ -def send_acknowledgement(): - """ - Function for sending an acknowledgment for a received message. - """ - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - msg=("Your Message was received.") # Message sent as a receipt confirmation - send_ack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket to send the acknowledgment - send_ack_socket.sendto(msg.encode('utf-8'),(right_neighbour,acknowledgement_port)) # Send the acknowledgment message to the right neighbor using the acknowledgement_port - send_ack_socket.close() # Close the socket after sending the message - -def receive_acknowledgement(msg, ip, so): - """ - Function for receiving acknowledgments. This section includes the system's response to messages not received between servers. - """ - global members_IP - global is_leader - global leader_ip - - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - recack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to receive acknowledgment messages - recack_socket.bind((ip_address,acknowledgement_port)) - recack_socket.settimeout(5) # Set a timeout for receiving acknowledgment --> Notwendig in welcher Höhe???? Testen?? - try: - data, addr = recack_socket.recvfrom(1024) # Attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: - print(f"No response received. Retrying message delivery.") # If no response is received, resend the message - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Open a temporary socket for resending the original message - temp_send_socket.sendto(msg,(ip,so)) - temp_send_socket.close() - recack_socket.settimeout(5) # Set a new timeout for acknowledgment - try: - data, addr = recack_socket.recvfrom(1024) # Second attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: # If the second attempt fails, handle server failure - print(f"No response again. Server is unreachable. Triggering ring update.") - recack_socket.close() - members_IP.remove(right_neighbour) # Remove the failed server from the member list - # Check if the failed server was the leader - if len(members_IP) == 1: - # If only one server remains, it becomes the leader - if leader_ip != ip_address: # Check if the leader IP is its own; only change is_leader to True if the server was not the leader before - leader_ip = ip_address # Set leder_ip to own IP - is_leader.set_value(True) - print("I am now the last server in this ring and therefore the leader.") - else: - # Update the ring and forward necessary messages - send_update_to_ring() - if right_neighbour == leader_ip: #check if failed server was the leader - start_election() - new_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the new right neighbor with updated ring - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - temp_send_socket.sendto(msg,(new_neighbour,so)) # Forward the original message to the new neighbor - temp_send_socket.close() - receive_acknowledgement(msg, new_neighbour, so) # Wait for acknowledgment from the new neighbor -########################### End - Acknowledgement ########################### - -########################### Start - Update ring ########################### -def lausche_update_Ring(): - """ - Listens for ring updates via UDP broadcasts and handles updates to the members_IP list. - """ - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for communication - sock.bind((ip_address, ringport)) # Bind the socket to the servers IP address and ring port for listening - while True: - try: - data, addr = sock.recvfrom(1024) # Receive data from the socket - members_IP2 = json.loads(data.decode()) # Decode the received JSON data to update the members list - if members_IP2 == members_IP: # Check if the received members list matches the current members list - print(f"Ring update has traveled through the ring.") - send_acknowledgement() # Send an acknowledgment for the received update - else: - members_IP = members_IP2 # Update the local members list - print(f"Ring update received: {members_IP}") - send_acknowledgement() # Send an acknowledgment for the received update - send_update_to_ring() # Forward the updated member list to the next neighbor - except json.JSONDecodeError: - print("Error decoding the JSON data.") # Handle errors in decoding the JSON data - -def send_update_to_ring(): - """ - Sends the updated members list to the next server in the ring. - """ - global members_IP - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) > 1:# If no right neighbor exists, there is no one to send the update to - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - data = json.dumps(members_IP).encode() # Serialize the members list into JSON format - sock.sendto(data, (right_neighbour , ringport)) # Send the members list to the right neighbor - sock.close()# Close the socket to free up resources - receive_acknowledgement(data, right_neighbour , ringport) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data to Ring: {e}") - -########################### End - Update ring ########################### - -########################### Start - Server Enters ########################### -def new_server_in_ring(): - """ - This function is executed by the Leader. It listens for incoming messages from servers attempting to join the network. - The Leader maintains the list of all IP addresses in the ring and updates the topology whenever a new server joins. - Topology Updates: The Leader ensures all servers in the network are aware of the latest ring structure. - """ - global members_UUID - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.bind(("0.0.0.0", enter_port)) # Bind the socket to the address "0.0.0.0" and the enter_port - print("Server is running and waiting for broadcast messages from new servers.") - while True: - data, addr = sock.recvfrom(1024) # Listen for messages from other servers - print(f"Message received from {addr}: {data.decode()}") - new_server_ip, new_server_port = addr # Extract the IP and port of the new server from the received address - new_IP = data.decode() - new_IP = new_IP.split(": ")[1] # Extract the IP address from the message - if new_IP in members_IP: # Check if the IP already exists. This might happen if a server temporarily lost connection. #######Tritt das überhaupt ein? In Gruppe diskutieren - msg = json.dumps(members_IP).encode() # If the server already exists, send the updated member list - else: - members_IP.append(new_IP) # If the server is new, add its IP to the list - msg = f"There are already servers. I am your leader: {ip_address}".encode() # Create a message for the new server - #AttributeError: 'bytes' object has no attribute 'encode'. Did you mean: 'decode'? - sock.sendto(msg, (new_IP, new_server_port)) # Send the greeting message back to the new server - print(f"The updated IP_Ring is: {members_IP}") - send_update_to_ring() # Update the ring topology - -def server_enters(): - """ - This function is used when a server wants to join the network. It sends a greeting message to the broadcast address and waits for a response from the Leader. - If no response is received, the server assumes the Leader role and starts managing the ring itself. - Broadcast Communication: This allows new servers to discover the Leader without knowing its specific IP address. - """ - global members_UUID - global leader_ip - - msg = f"I am new: {ip_address}".encode() # Greeting message from the new server - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.sendto(msg, (broadcast_ip, enter_port)) # Send the greeting message to the broadcast address using the enter_port - sock.settimeout(5) # Set a timeout to wait for a response --> ggf. anpassen, je nach geschwindigkeit mit Handy Internet - try: - data, addr = sock.recvfrom(1024) # receiving response - print(f"Antwort von {addr}: {data.decode()}") - sock.close() - my_leader = data.decode().split(": ")[1] # Extract the Leader's IP address from the response - leader_ip = my_leader # Set leder_ip to the received IP - except socket.timeout: - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") # If no answer is received the server sets itself as leader - members_UUID.append(my_ID) # Add itself as a participant in the ring - members_IP.append(ip_address) # Add itself as a participant in the ring - sock.close() - leader_ip = ip_address # Set leder_ip to own IP - print(leader_ip) - is_leader.set_value(True) # Mark itself as the Leader -########################### End - Server Enters ########################### - -########################### Start - Leader Election ########################### -def start_election(): - """ - Initiates an election by sending the server's UUID and IP address to its right neighbor in the ring. - Marks the server as participating in the election process and waits for acknowledgment. - """ - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - print("{} is starting an election.".format(myuuid)) - participating = True # Server marks itself as participating in the election - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode('utf-8') # Create the election message with the server's UUID and IP address - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to send the election message - send_socket.sendto(msg,(right_neighbour,election_port)) # Send msg to the right neighbor usinf the election_port - send_socket.close() - receive_acknowledgement(msg, right_neighbour, election_port) # Wait for acknowledgment - -def accept(group,erhaltene_uuid,erhaltene_ip): - """ - Function to handle election messages and determine the next steps. - If the message is part of an election (ELECTION), the server compares UUIDs to either forward, update, or declare itself as the leader. - If the message is a new leader announcement (NEW_LEAD), it updates the local leader information and forwards the message. - Leader Election: Based on comparing UUIDs, the server with the highest UUID becomes the leader. - Acknowledgment Mechanism: Ensures that messages are received and processed reliably. - """ - global leader_ip - global is_leader - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if group == ELECTION: # If the received message is part of an election - # Compare the received UUID with the server's own UUID - if erhaltene_uuid > myuuid: # Received UUID is greater, so forward the message without changes - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}: {erhaltene_ip}".encode() - if erhaltene_uuid < myuuid: # Received UUID is smaller, update with the server's own UUID and forward - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode() - if erhaltene_uuid == myuuid: # If the server receives its own UUID, it becomes the leader - print("{} starts acting as a leader!".format(myuuid)) - participating = False - leader_ip = ip_address # Set leader_ip to own IP - leader = myuuid #Set leader to own uuid - msg = f"{NEW_LEAD}: {myuuid}: {ip_address}".encode() - if leader_ip != ip_address: # Update leadership status if server was not already the leader bevor the election - is_leader.set_value(True) - if group == NEW_LEAD: # If the received message announces a new leader - if erhaltene_uuid == myuuid: # If the UUID matches, the server has already acknowledged - return - if erhaltene_uuid != myuuid: # Update the leader information and forward the new leader announcement - print("{} acknowledged new leader.".format(myuuid)) - if leader_ip == ip_address: # Check if this server was the Leader bevor the election and set is_leader to False - is_leader.set_value(False) - leader_ip = erhaltene_ip # Update leader_ip - leader = erhaltene_uuid # Update leader - msg = f"{NEW_LEAD}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - -def zuhören_election(): - """ - Listens for incoming election or leader messages on the configured election socket. - Decodes the message, sends an acknowledgment to the sender, and processes the message via the accept() function. - """ - sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - sock.bind((ip_address,election_port)) # Bind to the election socket - while True: - data,addr=sock.recvfrom(4096) # Receive data from other servers - übernahme = data.decode('utf-8') # Decode the received message - grouprec = int(übernahme.split(": ")[0]) # Extract group ID, UUID, and IP address from the message - erhaltene_ip = (übernahme.split(": ")[2]) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - send_acknowledgement() # Send acknowledgment back to the sender - accept(grouprec,erhaltene_uuid2,erhaltene_ip) # Process the election or new leader message -########################### End - Leader Election ########################### - -########################### Start - Process client messages ########################### -def process_hold_back_queue(): - """ - Processes messages in the hold-back queue in the correct order. - """ - while hold_back_queue: - message = hold_back_queue.popleft() # Remove the oldest message from the queue - message_id, decoded_message = message - if message_id not in processed_message_ids: - # Process or forward the message - print(f"Processing message: {decoded_message}") - broadcast(decoded_message) # Forward the message to all participants - processed_message_ids.add(message_id) # Mark the message as processed - -def broadcast(message): - """ - Sends a message to all participants in the network via broadcast. - """ - server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - full_message = f"{message}".encode() # Encode the message - server_socket2.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - server_socket2.close() - -def listen_client(): - """ - Listens for messages from clients, processes them, and broadcasts them to other participants. - """ - global last_five_messages - server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_socket.bind(('', client_broadcast_port)) # Bind to the broadcast port - while True: # Wait to receive a message from a client - try: - message, client_address = server_socket.recvfrom(4096) # Receive a message from a client - decoded_message = message.decode() # Decode the message - last_five_messages.append(decoded_message) # Store the message for Leader heartbeat - #print(f"Message stored: {last_three_messages}") # Only for debugging - message_id = decoded_message.split(":")[0] # Extract the unique message ID (UUID) from the decoded message - if message_id in processed_message_ids: # Check if the message has already been processed - continue # Skip if the message was already processed - hold_back_queue.append((message_id, decoded_message)) # Add the message to the hold-back queue - process_hold_back_queue() # Process messages in the hold-back queue - except socket.error as e: # Handle socket errors - print(f"An error occurred while listening: {e}") - break - except KeyboardInterrupt: # Handle server shutdown via keyboard interrupt ????? Funktioniert das?? - print("\nShutting down server...") - break -########################### End - Process client messages ########################### - -########################### Start - Leader Heartbeat ################################# -def forward_received_heartbeat_to_clients(): - """ - Forwards the last 5 received heartbeat messages separately to clients via UDP broadcast. - """ - server_socket3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket3.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - print("Starting to send the messages from the heartbeat.") - for message in received_heartbeat: - try: - print(f"Forwarding message: {message}") # Print the message being forwarded for debugging - full_message = f"{message}".encode() # Encode the message - server_socket3.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - except Exception as e: - print(f"Error forwarding message: {e}") - server_socket3.close() # Close the socket to free up resources - -def listen_heartbeat(): - """ - Listens for the leader's heartbeat and forwards it to the right neighbor in the ring. - """ - global received_heartbeat - global last_heartbeat_time - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats - sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening - while True: # Receive data from the socket - data, addr = sock.recvfrom(1024) # Receive data from the socket - print("Leader heartbeat received.") - send_acknowledgement() # Send acknowledgment for the received message - received_heartbeat = pickle.loads(data) # Deserialize the received data - print(received_heartbeat) # Debugging. Print the received messages - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - if leader_ip == ip_address: # If the server is the leader - if send_heartbeat==received_heartbeat: - print(f"My heartbeat has traveled through the ring.") - else: - print(f"Received a foreign heartbeat. I am forwarding the messages.") - forward_received_heartbeat_to_clients() - else: # Forward the heartbeat to the right neighbor - print("Heartbeat wird an den nachbarn gesendet") - right_neighbour = get_neighbour(members_IP, ip_address, 'right') - msg = pickle.dumps(received_heartbeat) - try: - sock_heart = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending Hearbeat - sock_heart.sendto(msg, (right_neighbour , heartbeat_port)) # Send heartbeat to the right neighbor - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - sock_heart.close() # Close the socket to free up resources - except Exception as e: # Handle errors during data transmission - print(f"Error sending data lsiten heartbeat: {e}") - - -def leader_send_heartbeat(): - """ - Sends the Heartbeat to the next server in the ring. - """ - global send_heartbeat - global last_heartbeat_time - while True: - send_heartbeat = last_five_messages # Set the send_heartbeat as the last five messages - msg = pickle.dumps(send_heartbeat) # Serialize the messages - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) == 1: # If only this server is part of the Ring it does not have to send an Heartbeat - print("I am the only server. There is no other Server to send a heartbeat to.") - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - else: # Send the heartbeat to the right neighbor - try: - sock_heart_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - sock_heart_send.sendto(msg, (right_neighbour , heartbeat_port)) # Send the members list to the right neighbor - sock_heart_send.close() - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data Leader_send_heartbeat: {e}") - #sock_heart_send.close() # Close the socket to free up resources - time.sleep(10) # Wait before sending the next heartbeat - -def monitor_heartbeat(): ################ Achtung Testen, ob auch der Leader in ein timeout kommen kann... ggf. vor removal f leader IP einfügen####### - """ - Monitors whether the last heartbeat was received within the timeout period. - """ - global members_IP - global leader_ip - while True: - time_since_last_heartbeat = time.time() - last_heartbeat_time # Calculate the time since the last heartbeat - if time_since_last_heartbeat > 18: - print(f"ERROR: No heartbeat received for 18 seconds! Leader is down.") - members_IP.remove(leader_ip) # Remove the Leader server from the member list - if len(members_IP) == 1: # If only one server remains, it becomes the leader - print("I am now the last server in this ring and therefore the leader.") - leader_ip = ip_address # Update the leader IP to the current server - is_leader.set_value(True) - else: - send_update_to_ring() # Update the ring and forward necessary messages - start_election() # Start leader election to determine new Leader - time.sleep(1) # Check for heartbeat status every second -########################### End - Leader Heartbeat ############################ - -########################### Start - Client Heartbeat ########################### -def leader_send_Client_heartbeat(): - """ - Broadcasts a heartbeat message to the clients, indicating the server is available. - """ - while True: - msg = ("Server is available.") # Define the heartbeat message - print("I am sending a heartbeat to the client.") # Debugging message indicating that the server is sending a heartbeat - server_client_heartbeat = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_client_heartbeat.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_client_heartbeat.sendto(msg.encode('utf-8'), (broadcast_ip, heartbeat_client_broadcast_port)) # # Send the broadcast message to the specified broadcast IP and port - server_client_heartbeat.close()# Close the socket to free up resources - time.sleep(5) # Wait for 15 seconds before sending the next heartbeat -########################### End - Client Heartbeat ########################### - -########################### Start - observation value changes leader ########################### -class VariableWatcher: # A utility class designed to monitor a variable's value and notify registered observers whenever the value changes. - def __init__(self): - self._value = None # Initializes the variable's value as None - self.observers = [] # Creates a list to store observers - def set_value(self, new_value): # Updates the variable and notifies observers. - self._value = new_value # Updates the variable's value - self.notify(new_value) # Notifies all observers about the new value - def add_observer(self, observer): - self.observers.append(observer) # Adds an observer to the list - def notify(self, new_value): - for observer in self.observers: # Iterates through all registered observers - observer(new_value) # Calls each observer with the new value - -def callback(new_value): - """ - This function is triggered when the `is_leader` value changes. - """ - if new_value: - print("I am now taking over leader responsibilities.") - thread7 = threading.Thread(target=listen_client) # Listens to client messages - thread8 = threading.Thread(target=new_server_in_ring) # Listens for election messages - thread9 = threading.Thread(target= leader_send_Client_heartbeat) # Start sending heartbeats to the client - thread10 = threading.Thread(target= leader_send_heartbeat) - thread7.start() - thread8.start() - thread9.start() - if len(received_heartbeat) > 0: # Check if there are values in received_heartbeat - print("I am sending the last 5 messages of the leader heartbeat to the clients.") - forward_received_heartbeat_to_clients() # Forward the heartbeat to the clients. - thread10.start() # Leader starts sending its heartbeat to its right neighbour. - else: - print("I am no longer the leader.") -########################### End - observation value changes leader ########################### - -if __name__ == "__main__": - # Create threads for different server operations - thread1 = threading.Thread(target=server_enters) # Handles server entry to the ring - thread2 = threading.Thread(target=lausche_update_Ring) # Listens for ring updates - #thread3 = threading.Thread(target=frage_benutzer) # Prompts the user for action --> Only for testing! - thread4 = threading.Thread(target=zuhören_election) # Listens for election messages - thread5 = threading.Thread(target=listen_heartbeat) # Listens for leader heartbeat - thread6 = threading.Thread(target=monitor_heartbeat) # Monitord the leader heartbeat - # Start all threads - thread1.start() - thread2.start() - #thread3.start() - thread4.start() - thread5.start() - thread6.start() - - is_leader = VariableWatcher() # Create an instance of VariableWatcher to observe changes in leader status - is_leader.add_observer(callback) # Add the callback function as an observer for changes in `is_leader` \ No newline at end of file diff --git "a/2025-01-10_Server_V8_ohne_weiteren_\303\204nderungen.py" "b/2025-01-10_Server_V8_ohne_weiteren_\303\204nderungen.py" deleted file mode 100644 index 22ef8c0565fe24272e2c4f428a9b1c8e1323f0ab..0000000000000000000000000000000000000000 --- "a/2025-01-10_Server_V8_ohne_weiteren_\303\204nderungen.py" +++ /dev/null @@ -1,547 +0,0 @@ - -from inspect import _empty -import time -import threading -from uuid import uuid4 -import socket -import uuid -import pickle -import json -import multiprocessing -import os -from multiprocessing import Manager -from collections import deque - -# Global variables to manage ring members and their information -global members_UUID -global members_IP -# Initialize lists to keep track of members -members_UUID = [] # List for UUIDs of members in the ring -members_IP = [] # List for IP addresses of members in the ring -# Network and port configurations -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -enter_port = 12348 #Port that is used for the discovery of new server participants -ringport = 12343 -election_port = 12345 -client_broadcast_port = 55555 #client sends, server listens -client_broadcast_port2 = 33333 #server sends, client listens -acknowledgement_port = 22222 -heartbeat_port = 44444 -heartbeat_client_broadcast_port = 11111 -# Unique identification for this server -myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 -hostname = socket.gethostname() -ip_address = socket.gethostbyname(hostname) # Retrieves the hostname of the current machine -#ip_address = "127.0.0.1" ########verwenden zum Testen auf einem Gerät -# Leader election-related variables -participating = False # Indicates whether the server is currently participating in an election -is_leader = False # Boolean flag to indicate if the server is the leader -Leader = False # Alternate flag to indicate leader status (can be consolidated with is_leader) -ELECTION = 0 # Message type for initiating an election -NEW_LEAD = 1 # Message type for announcing a new leader -leader_ip = 'unknown' # Stores the IP address of the current leader; default is 'unknown' -# Heartbeat related variables -last_five_messages = deque(maxlen=5) # Initialization of message storage (last 5 messages) -send_heartbeat = deque() -received_heartbeat = deque() -last_heartbeat_time = time.time() - -# variables for Listen to Client -hold_back_queue = deque() # A double-ended queue (deque) to store messages that need to be temporarily held back before they are processed. -processed_message_ids = set() # A set to track the IDs of messages that have already been processed. This helps avoid duplicate processing. - -########################### Start - Neighbour ########################### -def get_neighbour(members_IP, current_member_ip, direction='left'): - """ - Determines the neighbor of a server in a circular ring topology based on the direction. - """ - current_member_index = members_IP.index(current_member_ip) if current_member_ip in members_IP else -1 # Find the index of the current member in the list. If not found, set to -1. - if current_member_index != -1: # Determine the neighbor to the 'left' - if direction == 'left': # Determine the neighbor to the 'left' (next in the ring) - if current_member_index + 1 == len(members_IP): # If the current member is the last in the list, wrap around to the first - return members_IP[0] # Return the first member in the list - else: - return members_IP[current_member_index + 1] # Return the next member in the list - else: # Determine the neighbor to the 'right' - if current_member_index - 1 < 0: # If the current member is the first in the list, wrap around to the last - return members_IP[len(members_IP) - 1] # Return the last member in the list - else: - return members_IP[current_member_index - 1] # Return the previous member in the list - else: - return None # If the current member IP is not found in the list, return None -########################### End - Neighbour ########################### - -########################### Start - Acknowledgement ############################ -def send_acknowledgement(): - """ - Function for sending an acknowledgment for a received message. - """ - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'left') # Determine the right neighbor based on the current ring structure - msg=("Your Message was received.") # Message sent as a receipt confirmation - send_ack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket to send the acknowledgment - send_ack_socket.sendto(msg.encode('utf-8'),(right_neighbour,acknowledgement_port)) # Send the acknowledgment message to the right neighbor using the acknowledgement_port - send_ack_socket.close() # Close the socket after sending the message - -def receive_acknowledgement(msg, ip, so): - """ - Function for receiving acknowledgments. This section includes the system's response to messages not received between servers. - """ - global members_IP - global is_leader - global leader_ip - - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - recack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to receive acknowledgment messages - recack_socket.bind((ip_address,acknowledgement_port)) - recack_socket.settimeout(5) # Set a timeout for receiving acknowledgment --> Notwendig in welcher Höhe???? Testen?? - try: - data, addr = recack_socket.recvfrom(1024) # Attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: - print(f"No response received. Retrying message delivery.") # If no response is received, resend the message - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Open a temporary socket for resending the original message - temp_send_socket.sendto(msg,(ip,so)) - temp_send_socket.close() - recack_socket.settimeout(5) # Set a new timeout for acknowledgment - try: - data, addr = recack_socket.recvfrom(1024) # Second attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: # If the second attempt fails, handle server failure - print(f"No response again. Server is unreachable. Triggering ring update.") - recack_socket.close() - members_IP.remove(right_neighbour) # Remove the failed server from the member list - # Check if the failed server was the leader - if len(members_IP) == 1: - # If only one server remains, it becomes the leader - if leader_ip != ip_address: # Check if the leader IP is its own; only change is_leader to True if the server was not the leader before - leader_ip = ip_address # Set leder_ip to own IP - is_leader.set_value(True) - print("I am now the last server in this ring and therefore the leader.") - else: - # Update the ring and forward necessary messages - send_update_to_ring() - if right_neighbour == leader_ip: #check if failed server was the leader - start_election() - new_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the new right neighbor with updated ring - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - temp_send_socket.sendto(msg,(new_neighbour,so)) # Forward the original message to the new neighbor - temp_send_socket.close() - receive_acknowledgement(msg, new_neighbour, so) # Wait for acknowledgment from the new neighbor -########################### End - Acknowledgement ########################### - -########################### Start - Update ring ########################### -def lausche_update_Ring(): - """ - Listens for ring updates via UDP broadcasts and handles updates to the members_IP list. - """ - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for communication - sock.bind((ip_address, ringport)) # Bind the socket to the servers IP address and ring port for listening - while True: - try: - data, addr = sock.recvfrom(1024) # Receive data from the socket - members_IP2 = json.loads(data.decode()) # Decode the received JSON data to update the members list - if members_IP2 == members_IP: # Check if the received members list matches the current members list - print(f"Ring update has traveled through the ring.") - send_acknowledgement() # Send an acknowledgment for the received update - else: - members_IP = members_IP2 # Update the local members list - print(f"Ring update received: {members_IP}") - send_acknowledgement() # Send an acknowledgment for the received update - send_update_to_ring() # Forward the updated member list to the next neighbor - except json.JSONDecodeError: - print("Error decoding the JSON data.") # Handle errors in decoding the JSON data - -def send_update_to_ring(): - """ - Sends the updated members list to the next server in the ring. - """ - global members_IP - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) > 1:# If no right neighbor exists, there is no one to send the update to - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - data = json.dumps(members_IP).encode() # Serialize the members list into JSON format - sock.sendto(data, (right_neighbour , ringport)) # Send the members list to the right neighbor - sock.close()# Close the socket to free up resources - receive_acknowledgement(data, right_neighbour , ringport) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data to Ring: {e}") -########################### End - Update ring ########################### - -########################### Start - Server Enters ########################### -def new_server_in_ring(): - """ - This function is executed by the Leader. It listens for incoming messages from servers attempting to join the network. - The Leader maintains the list of all IP addresses in the ring and updates the topology whenever a new server joins. - Topology Updates: The Leader ensures all servers in the network are aware of the latest ring structure. - """ - global members_UUID - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.bind(("0.0.0.0", enter_port)) # Bind the socket to the address "0.0.0.0" and the enter_port - print("Server is running and waiting for broadcast messages from new servers.") - while True: - data, addr = sock.recvfrom(1024) # Listen for messages from other servers - print(f"Message received from {addr}: {data.decode()}") - new_server_ip, new_server_port = addr # Extract the IP and port of the new server from the received address - new_IP = data.decode() - new_IP = new_IP.split(": ")[1] # Extract the IP address from the message - if new_IP in members_IP: # Check if the IP already exists. This might happen if a server temporarily lost connection. #######Tritt das überhaupt ein? In Gruppe diskutieren - msg = json.dumps(members_IP).encode() # If the server already exists, send the updated member list - else: - members_IP.append(new_IP) # If the server is new, add its IP to the list - msg = f"There are already servers. I am your leader: {ip_address}".encode() # Create a message for the new server - #AttributeError: 'bytes' object has no attribute 'encode'. Did you mean: 'decode'? - sock.sendto(msg, (new_IP, new_server_port)) # Send the greeting message back to the new server - print(f"The updated IP_Ring is: {members_IP}") - send_update_to_ring() # Update the ring topology - -def server_enters(): - """ - This function is used when a server wants to join the network. It sends a greeting message to the broadcast address and waits for a response from the Leader. - If no response is received, the server assumes the Leader role and starts managing the ring itself. - Broadcast Communication: This allows new servers to discover the Leader without knowing its specific IP address. - """ - global members_UUID - global leader_ip - - msg = f"I am new: {ip_address}".encode() # Greeting message from the new server - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.sendto(msg, (broadcast_ip, enter_port)) # Send the greeting message to the broadcast address using the enter_port - sock.settimeout(5) # Set a timeout to wait for a response --> ggf. anpassen, je nach geschwindigkeit mit Handy Internet - try: - data, addr = sock.recvfrom(1024) # receiving response - print(f"Antwort von {addr}: {data.decode()}") - sock.close() - my_leader = data.decode().split(": ")[1] # Extract the Leader's IP address from the response - leader_ip = my_leader # Set leder_ip to the received IP - except socket.timeout: - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") # If no answer is received the server sets itself as leader - members_UUID.append(my_ID) # Add itself as a participant in the ring - members_IP.append(ip_address) # Add itself as a participant in the ring - sock.close() - leader_ip = ip_address # Set leder_ip to own IP - print(leader_ip) - is_leader.set_value(True) # Mark itself as the Leader -########################### End - Server Enters ########################### - -########################### Start - Leader Election ########################### -def start_election(): - """ - Initiates an election by sending the server's UUID and IP address to its right neighbor in the ring. - Marks the server as participating in the election process and waits for acknowledgment. - """ - global participating - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - print("{} is starting an election.".format(myuuid)) - participating = True # Server marks itself as participating in the election - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode('utf-8') # Create the election message with the server's UUID and IP address - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to send the election message - send_socket.sendto(msg,(right_neighbour,election_port)) # Send msg to the right neighbor usinf the election_port - send_socket.close() - receive_acknowledgement(msg, right_neighbour, election_port) # Wait for acknowledgment - -def accept(group,erhaltene_uuid,erhaltene_ip): - """ - Function to handle election messages and determine the next steps. - If the message is part of an election (ELECTION), the server compares UUIDs to either forward, update, or declare itself as the leader. - If the message is a new leader announcement (NEW_LEAD), it updates the local leader information and forwards the message. - Leader Election: Based on comparing UUIDs, the server with the highest UUID becomes the leader. - Acknowledgment Mechanism: Ensures that messages are received and processed reliably. - """ - global leader_ip - global is_leader - global last_heartbeat_time - last_heartbeat_time = time.time() - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if group == ELECTION: # If the received message is part of an election - # Compare the received UUID with the server's own UUID - if erhaltene_uuid > myuuid: # Received UUID is greater, so forward the message without changes - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}: {erhaltene_ip}".encode() - if erhaltene_uuid < myuuid: # Received UUID is smaller, update with the server's own UUID and forward - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode() - if erhaltene_uuid == myuuid: # If the server receives its own UUID, it becomes the leader - print("{} starts acting as a leader!".format(myuuid)) - participating = False - leader_ip = ip_address # Set leader_ip to own IP - leader = myuuid #Set leader to own uuid - msg = f"{NEW_LEAD}: {myuuid}: {ip_address}".encode() - if leader_ip != ip_address: # Update leadership status if server was not already the leader bevor the election - is_leader.set_value(True) - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - if group == NEW_LEAD: # If the received message announces a new leader - if erhaltene_uuid == myuuid: # If the UUID matches, the server has already acknowledged - return - if erhaltene_uuid != myuuid: # Update the leader information and forward the new leader announcement - print("{} acknowledged new leader.".format(myuuid)) - if leader_ip == ip_address: # Check if this server was the Leader bevor the election and set is_leader to False - is_leader.set_value(False) - leader_ip = erhaltene_ip # Update leader_ip - leader = erhaltene_uuid # Update leader - msg = f"{NEW_LEAD}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - -def zuhören_election(): - """ - Listens for incoming election or leader messages on the configured election socket. - Decodes the message, sends an acknowledgment to the sender, and processes the message via the accept() function. - """ - sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - sock.bind((ip_address,election_port)) # Bind to the election socket - while True: - data,addr=sock.recvfrom(4096) # Receive data from other servers - übernahme = data.decode('utf-8') # Decode the received message - grouprec = int(übernahme.split(": ")[0]) # Extract group ID, UUID, and IP address from the message - erhaltene_ip = (übernahme.split(": ")[2]) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - send_acknowledgement() # Send acknowledgment back to the sender - accept(grouprec,erhaltene_uuid2,erhaltene_ip) # Process the election or new leader message -########################### End - Leader Election ########################### - -########################### Start - Process client messages ########################### -def process_hold_back_queue(): - """ - Processes messages in the hold-back queue in the correct order. - """ - while hold_back_queue: - message = hold_back_queue.popleft() # Remove the oldest message from the queue - message_id, decoded_message = message - if message_id not in processed_message_ids: - # Process or forward the message - print(f"Processing message: {decoded_message}") - broadcast(decoded_message) # Forward the message to all participants - processed_message_ids.add(message_id) # Mark the message as processed - -def broadcast(message): - """ - Sends a message to all participants in the network via broadcast. - """ - server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - full_message = f"{message}".encode() # Encode the message - server_socket2.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - server_socket2.close() - -def listen_client(): - """ - Listens for messages from clients, processes them, and broadcasts them to other participants. - """ - global last_five_messages - server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_socket.bind(('', client_broadcast_port)) # Bind to the broadcast port - while True: # Wait to receive a message from a client - try: - message, client_address = server_socket.recvfrom(4096) # Receive a message from a client - decoded_message = message.decode() # Decode the message - last_five_messages.append(decoded_message) # Store the message for Leader heartbeat - #print(f"Message stored: {last_three_messages}") # Only for debugging - message_id = decoded_message.split(":")[0] # Extract the unique message ID (UUID) from the decoded message - if message_id in processed_message_ids: # Check if the message has already been processed - continue # Skip if the message was already processed - hold_back_queue.append((message_id, decoded_message)) # Add the message to the hold-back queue - process_hold_back_queue() # Process messages in the hold-back queue - except socket.error as e: # Handle socket errors - print(f"An error occurred while listening: {e}") - break - except KeyboardInterrupt: # Handle server shutdown via keyboard interrupt ????? Funktioniert das?? - print("\nShutting down server...") - break -########################### End - Process client messages ########################### - -########################### Start - Leader Heartbeat ################################# -def forward_received_heartbeat_to_clients(): - """ - Forwards the last 5 received heartbeat messages separately to clients via UDP broadcast. - """ - server_socket3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket3.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - print("Starting to send the messages from the heartbeat.") - for message in received_heartbeat: - try: - print(f"Forwarding message: {message}") # Print the message being forwarded for debugging - full_message = f"{message}".encode() # Encode the message - server_socket3.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - except Exception as e: - print(f"Error forwarding message: {e}") - server_socket3.close() # Close the socket to free up resources - -def listen_heartbeat(): - """ - Listens for the leader's heartbeat and forwards it to the right neighbor in the ring. - """ - global received_heartbeat - global last_heartbeat_time - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats - sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening - while True: # Receive data from the socket - data, addr = sock.recvfrom(1024) # Receive data from the socket - print("Leader heartbeat received.") - send_acknowledgement() # Send acknowledgment for the received message - received_heartbeat = pickle.loads(data) # Deserialize the received data - print(received_heartbeat) # Debugging. Print the received messages - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - if leader_ip == ip_address: # If the server is the leader - if send_heartbeat==received_heartbeat: - print(f"My heartbeat has traveled through the ring.") - else: - print(f"Received a foreign heartbeat. I am forwarding the messages.") - forward_received_heartbeat_to_clients() - else: # Forward the heartbeat to the right neighbor - print("Heartbeat wird an den nachbarn gesendet") - right_neighbour = get_neighbour(members_IP, ip_address, 'right') - msg = pickle.dumps(received_heartbeat) - try: - sock_heart = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending Hearbeat - sock_heart.sendto(msg, (right_neighbour , heartbeat_port)) # Send heartbeat to the right neighbor - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - sock_heart.close() # Close the socket to free up resources - except Exception as e: # Handle errors during data transmission - print(f"Error sending data lsiten heartbeat: {e}") - - -def leader_send_heartbeat(): - """ - Sends the Heartbeat to the next server in the ring. - """ - global send_heartbeat - global last_heartbeat_time - while True: - send_heartbeat = last_five_messages # Set the send_heartbeat as the last five messages - msg = pickle.dumps(send_heartbeat) # Serialize the messages - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) == 1: # If only this server is part of the Ring it does not have to send an Heartbeat - print("I am the only server. There is no other Server to send a heartbeat to.") - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - else: # Send the heartbeat to the right neighbor - try: - sock_heart_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - sock_heart_send.sendto(msg, (right_neighbour , heartbeat_port)) # Send the members list to the right neighbor - sock_heart_send.close() - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data Leader_send_heartbeat: {e}") - #sock_heart_send.close() # Close the socket to free up resources - time.sleep(5) # Wait before sending the next heartbeat - -def monitor_heartbeat(): ################ Achtung Testen, ob auch der Leader in ein timeout kommen kann... ggf. vor removal f leader IP einfügen####### - """ - Monitors whether the last heartbeat was received within the timeout period. - """ - global members_IP - global leader_ip - global last_heartbeat_time - while True: - time_since_last_heartbeat = time.time() - last_heartbeat_time # Calculate the time since the last heartbeat - if time_since_last_heartbeat > 18: - print(f"ERROR: No heartbeat received for 18 seconds! Leader is down.") - members_IP.remove(leader_ip) # Remove the Leader server from the member list - if len(members_IP) == 1: # If only one server remains, it becomes the leader - print("I am now the last server in this ring and therefore the leader.") - leader_ip = ip_address # Update the leader IP to the current server - is_leader.set_value(True) - else: - send_update_to_ring() # Update the ring and forward necessary messages - start_election() # Start leader election to determine new Leader - time.sleep(1) # Check for heartbeat status every second -########################### End - Leader Heartbeat ############################ - -########################### Start - Client Heartbeat ########################### -def leader_send_Client_heartbeat(): - """ - Broadcasts a heartbeat message to the clients, indicating the server is available. - """ - while True: - msg = ("Server is available.") # Define the heartbeat message - print("I am sending a heartbeat to the client.") # Debugging message indicating that the server is sending a heartbeat - server_client_heartbeat = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_client_heartbeat.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_client_heartbeat.sendto(msg.encode('utf-8'), (broadcast_ip, heartbeat_client_broadcast_port)) # # Send the broadcast message to the specified broadcast IP and port - server_client_heartbeat.close()# Close the socket to free up resources - time.sleep(5) # Wait for 15 seconds before sending the next heartbeat -########################### End - Client Heartbeat ########################### - -########################### Start - observation value changes leader ########################### -class VariableWatcher: # A utility class designed to monitor a variable's value and notify registered observers whenever the value changes. - def __init__(self): - self._value = None # Initializes the variable's value as None - self.observers = [] # Creates a list to store observers - def set_value(self, new_value): # Updates the variable and notifies observers. - self._value = new_value # Updates the variable's value - self.notify(new_value) # Notifies all observers about the new value - def add_observer(self, observer): - self.observers.append(observer) # Adds an observer to the list - def notify(self, new_value): - for observer in self.observers: # Iterates through all registered observers - observer(new_value) # Calls each observer with the new value - -def callback(new_value): - """ - This function is triggered when the `is_leader` value changes. - """ - if new_value: - print("I am now taking over leader responsibilities.") - thread7 = threading.Thread(target=listen_client) # Listens to client messages - thread8 = threading.Thread(target=new_server_in_ring) # Listens for election messages - thread9 = threading.Thread(target= leader_send_Client_heartbeat) # Start sending heartbeats to the client - thread10 = threading.Thread(target= leader_send_heartbeat) - thread7.start() - thread8.start() - thread9.start() - if len(received_heartbeat) > 0: # Check if there are values in received_heartbeat - print("I am sending the last 5 messages of the leader heartbeat to the clients.") - forward_received_heartbeat_to_clients() # Forward the heartbeat to the clients. - thread10.start() # Leader starts sending its heartbeat to its right neighbour. - else: - print("I am no longer the leader.") -########################### End - observation value changes leader ########################### - -########################### Only for Testing ########################## -####################################################################### -####################################################################### -def frage_benutzer(): # A test function that prompts the user to decide whether to execute the election process - antwort = input("Möchten Sie die Funktion ausführen? (Ja/Nein): ").strip().lower() - if antwort == 'ja': - start_election() - else: - print("Die Funktion wurde nicht ausgeführt.") -####################################################################### -####################################################################### -####################################################################### - -if __name__ == "__main__": - # Create threads for different server operations - thread1 = threading.Thread(target=server_enters) # Handles server entry to the ring - thread2 = threading.Thread(target=lausche_update_Ring) # Listens for ring updates - thread3 = threading.Thread(target=frage_benutzer) # Prompts the user for action --> Only for testing! - thread4 = threading.Thread(target=zuhören_election) # Listens for election messages - thread5 = threading.Thread(target=listen_heartbeat) # Listens for leader heartbeat - thread6 = threading.Thread(target=monitor_heartbeat) # Monitord the leader heartbeat - # Start all threads - thread1.start() - thread2.start() - thread3.start() - thread4.start() - thread5.start() - thread6.start() - - is_leader = VariableWatcher() # Create an instance of VariableWatcher to observe changes in leader status - is_leader.add_observer(callback) # Add the callback function as an observer for changes in `is_leader` \ No newline at end of file diff --git a/2025-01-10_Server_V9_mit_reuseaddr.py b/2025-01-10_Server_V9_mit_reuseaddr.py deleted file mode 100644 index c670b4cd256deaee8a3e7a04a79fc3954c2f4719..0000000000000000000000000000000000000000 --- a/2025-01-10_Server_V9_mit_reuseaddr.py +++ /dev/null @@ -1,558 +0,0 @@ - -from inspect import _empty -import time -import threading -from uuid import uuid4 -import socket -import uuid -import pickle -import json -import multiprocessing -import os -from multiprocessing import Manager -from collections import deque - -# Global variables to manage ring members and their information -global members_UUID -global members_IP -global last_heartbeat_time -# Initialize lists to keep track of members -members_UUID = [] # List for UUIDs of members in the ring -members_IP = [] # List for IP addresses of members in the ring -# Network and port configurations -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -enter_port = 12348 #Port that is used for the discovery of new server participants -ringport = 12343 -election_port = 12345 -client_broadcast_port = 55555 #client sends, server listens -client_broadcast_port2 = 33333 #server sends, client listens -acknowledgement_port = 22222 -heartbeat_port = 44444 -heartbeat_client_broadcast_port = 11111 -# Unique identification for this server -myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 -hostname = socket.gethostname() -ip_address = socket.gethostbyname(hostname) # Retrieves the hostname of the current machine -#ip_address = "127.0.0.1" ########verwenden zum Testen auf einem Gerät -# Leader election-related variables -participating = False # Indicates whether the server is currently participating in an election -is_leader = False # Boolean flag to indicate if the server is the leader -Leader = False # Alternate flag to indicate leader status (can be consolidated with is_leader) -ELECTION = 0 # Message type for initiating an election -NEW_LEAD = 1 # Message type for announcing a new leader -leader_ip = 'unknown' # Stores the IP address of the current leader; default is 'unknown' -# Heartbeat related variables -last_five_messages = deque(maxlen=5) # Initialization of message storage (last 5 messages) -send_heartbeat = deque() -received_heartbeat = deque() -last_heartbeat_time = time.time() - -# variables for Listen to Client -hold_back_queue = deque() # A double-ended queue (deque) to store messages that need to be temporarily held back before they are processed. -processed_message_ids = set() # A set to track the IDs of messages that have already been processed. This helps avoid duplicate processing. - -########################### Start - Neighbour ########################### -def get_neighbour(members_IP, current_member_ip, direction='left'): - """ - Determines the neighbor of a server in a circular ring topology based on the direction. - """ - current_member_index = members_IP.index(current_member_ip) if current_member_ip in members_IP else -1 # Find the index of the current member in the list. If not found, set to -1. - if current_member_index != -1: # Determine the neighbor to the 'left' - if direction == 'left': # Determine the neighbor to the 'left' (next in the ring) - if current_member_index + 1 == len(members_IP): # If the current member is the last in the list, wrap around to the first - return members_IP[0] # Return the first member in the list - else: - return members_IP[current_member_index + 1] # Return the next member in the list - else: # Determine the neighbor to the 'right' - if current_member_index - 1 < 0: # If the current member is the first in the list, wrap around to the last - return members_IP[len(members_IP) - 1] # Return the last member in the list - else: - return members_IP[current_member_index - 1] # Return the previous member in the list - else: - return None # If the current member IP is not found in the list, return None -########################### End - Neighbour ########################### - -########################### Start - Acknowledgement ############################ -def send_acknowledgement(): - """ - Function for sending an acknowledgment for a received message. - """ - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'left') # Determine the right neighbor based on the current ring structure - msg=("Your Message was received.") # Message sent as a receipt confirmation - send_ack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket to send the acknowledgment - send_ack_socket.sendto(msg.encode('utf-8'),(right_neighbour,acknowledgement_port)) # Send the acknowledgment message to the right neighbor using the acknowledgement_port - send_ack_socket.close() # Close the socket after sending the message - -def receive_acknowledgement(msg, ip, so): - """ - Function for receiving acknowledgments. This section includes the system's response to messages not received between servers. - """ - global members_IP - global is_leader - global leader_ip - - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - recack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to receive acknowledgment messages - recack_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - recack_socket.bind((ip_address,acknowledgement_port)) - recack_socket.settimeout(2) # Set a timeout for receiving acknowledgment --> Notwendig in welcher Höhe???? Testen?? - try: - data, addr = recack_socket.recvfrom(1024) # Attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: - print(f"No response received. Retrying message delivery.") # If no response is received, resend the message - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Open a temporary socket for resending the original message - temp_send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - temp_send_socket.sendto(msg,(ip,so)) - temp_send_socket.close() - recack_socket.settimeout(2) # Set a new timeout for acknowledgment - try: - data, addr = recack_socket.recvfrom(1024) # Second attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: # If the second attempt fails, handle server failure - print(f"No response again. Server is unreachable. Triggering ring update.") - recack_socket.close() - members_IP.remove(right_neighbour) # Remove the failed server from the member list - # Check if the failed server was the leader - if len(members_IP) == 1: - # If only one server remains, it becomes the leader - if leader_ip != ip_address: # Check if the leader IP is its own; only change is_leader to True if the server was not the leader before - leader_ip = ip_address # Set leder_ip to own IP - is_leader.set_value(True) - print("I am now the last server in this ring and therefore the leader.") - else: - # Update the ring and forward necessary messages - send_update_to_ring() - if right_neighbour == leader_ip: #check if failed server was the leader - start_election() - new_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the new right neighbor with updated ring - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - temp_send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - temp_send_socket.sendto(msg,(new_neighbour,so)) # Forward the original message to the new neighbor - temp_send_socket.close() - receive_acknowledgement(msg, new_neighbour, so) # Wait for acknowledgment from the new neighbor -########################### End - Acknowledgement ########################### - -########################### Start - Update ring ########################### -def lausche_update_Ring(): - """ - Listens for ring updates via UDP broadcasts and handles updates to the members_IP list. - """ - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for communication - sock.bind((ip_address, ringport)) # Bind the socket to the servers IP address and ring port for listening - while True: - try: - data, addr = sock.recvfrom(1024) # Receive data from the socket - members_IP2 = json.loads(data.decode()) # Decode the received JSON data to update the members list - if members_IP2 == members_IP: # Check if the received members list matches the current members list - print(f"Ring update has traveled through the ring.") - send_acknowledgement() # Send an acknowledgment for the received update - else: - members_IP = members_IP2 # Update the local members list - print(f"Ring update received: {members_IP}") - send_acknowledgement() # Send an acknowledgment for the received update - send_update_to_ring() # Forward the updated member list to the next neighbor - except json.JSONDecodeError: - print("Error decoding the JSON data.") # Handle errors in decoding the JSON data - -def send_update_to_ring(): - """ - Sends the updated members list to the next server in the ring. - """ - global members_IP - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) > 1:# If no right neighbor exists, there is no one to send the update to - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - data = json.dumps(members_IP).encode() # Serialize the members list into JSON format - sock.sendto(data, (right_neighbour , ringport)) # Send the members list to the right neighbor - sock.close()# Close the socket to free up resources - receive_acknowledgement(data, right_neighbour , ringport) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data to Ring: {e}") -########################### End - Update ring ########################### - -########################### Start - Server Enters ########################### -def new_server_in_ring(): - """ - This function is executed by the Leader. It listens for incoming messages from servers attempting to join the network. - The Leader maintains the list of all IP addresses in the ring and updates the topology whenever a new server joins. - Topology Updates: The Leader ensures all servers in the network are aware of the latest ring structure. - """ - global members_UUID - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.bind(("0.0.0.0", enter_port)) # Bind the socket to the address "0.0.0.0" and the enter_port - print("Server is running and waiting for broadcast messages from new servers.") - while True: - data, addr = sock.recvfrom(1024) # Listen for messages from other servers - print(f"Message received from {addr}: {data.decode()}") - new_server_ip, new_server_port = addr # Extract the IP and port of the new server from the received address - new_IP = data.decode() - new_IP = new_IP.split(": ")[1] # Extract the IP address from the message - if new_IP in members_IP: # Check if the IP already exists. This might happen if a server temporarily lost connection. #######Tritt das überhaupt ein? In Gruppe diskutieren - msg = json.dumps(members_IP).encode() # If the server already exists, send the updated member list - else: - members_IP.append(new_IP) # If the server is new, add its IP to the list - msg = f"There are already servers. I am your leader: {ip_address}".encode() # Create a message for the new server - #AttributeError: 'bytes' object has no attribute 'encode'. Did you mean: 'decode'? - sock.sendto(msg, (new_IP, new_server_port)) # Send the greeting message back to the new server - print(f"The updated IP_Ring is: {members_IP}") - send_update_to_ring() # Update the ring topology - -def server_enters(): - """ - This function is used when a server wants to join the network. It sends a greeting message to the broadcast address and waits for a response from the Leader. - If no response is received, the server assumes the Leader role and starts managing the ring itself. - Broadcast Communication: This allows new servers to discover the Leader without knowing its specific IP address. - """ - global members_UUID - global leader_ip - - msg = f"I am new: {ip_address}".encode() # Greeting message from the new server - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.sendto(msg, (broadcast_ip, enter_port)) # Send the greeting message to the broadcast address using the enter_port - sock.settimeout(5) # Set a timeout to wait for a response --> ggf. anpassen, je nach geschwindigkeit mit Handy Internet - try: - data, addr = sock.recvfrom(1024) # receiving response - print(f"Antwort von {addr}: {data.decode()}") - sock.close() - my_leader = data.decode().split(": ")[1] # Extract the Leader's IP address from the response - leader_ip = my_leader # Set leder_ip to the received IP - except socket.timeout: - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") # If no answer is received the server sets itself as leader - members_UUID.append(my_ID) # Add itself as a participant in the ring - members_IP.append(ip_address) # Add itself as a participant in the ring - sock.close() - leader_ip = ip_address # Set leder_ip to own IP - print(leader_ip) - is_leader.set_value(True) # Mark itself as the Leader -########################### End - Server Enters ########################### - -########################### Start - Leader Election ########################### -def start_election(): - """ - Initiates an election by sending the server's UUID and IP address to its right neighbor in the ring. - Marks the server as participating in the election process and waits for acknowledgment. - """ - global participating - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - print("{} is starting an election.".format(myuuid)) - participating = True # Server marks itself as participating in the election - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode('utf-8') # Create the election message with the server's UUID and IP address - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to send the election message - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send msg to the right neighbor usinf the election_port - send_socket.close() - receive_acknowledgement(msg, right_neighbour, election_port) # Wait for acknowledgment - -def accept(group,erhaltene_uuid,erhaltene_ip): - """ - Function to handle election messages and determine the next steps. - If the message is part of an election (ELECTION), the server compares UUIDs to either forward, update, or declare itself as the leader. - If the message is a new leader announcement (NEW_LEAD), it updates the local leader information and forwards the message. - Leader Election: Based on comparing UUIDs, the server with the highest UUID becomes the leader. - Acknowledgment Mechanism: Ensures that messages are received and processed reliably. - """ - global leader_ip - global is_leader - global last_heartbeat_time - global participating - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if group == ELECTION: # If the received message is part of an election - # Compare the received UUID with the server's own UUID - if erhaltene_uuid > myuuid: # Received UUID is greater, so forward the message without changes - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - elif erhaltene_uuid < myuuid and participating==False: # Received UUID is smaller, update with the server's own UUID and forward - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - if erhaltene_uuid == myuuid: # If the server receives its own UUID, it becomes the leader - print("{} starts acting as a leader!".format(myuuid)) - participating = False - leader_ip = ip_address # Set leader_ip to own IP - leader = myuuid #Set leader to own uuid - msg = f"{NEW_LEAD}: {myuuid}: {ip_address}".encode() - #if leader_ip != ip_address: # Update leadership status if server was not already the leader bevor the election - is_leader.set_value(True) - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - - if group == NEW_LEAD: # If the received message announces a new leader - if erhaltene_uuid == myuuid: # If the UUID matches, the server has already acknowledged - return - if erhaltene_uuid != myuuid: # Update the leader information and forward the new leader announcement - print("{} acknowledged new leader.".format(myuuid)) - if leader_ip == ip_address: # Check if this server was the Leader bevor the election and set is_leader to False - is_leader.set_value(False) - leader_ip = erhaltene_ip # Update leader_ip - leader = erhaltene_uuid # Update leader - msg = f"{NEW_LEAD}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - last_heartbeat_time = time.time() - -def zuhören_election(): - """ - Listens for incoming election or leader messages on the configured election socket. - Decodes the message, sends an acknowledgment to the sender, and processes the message via the accept() function. - """ - sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - sock.bind((ip_address,election_port)) # Bind to the election socket - while True: - data,addr=sock.recvfrom(4096) # Receive data from other servers - übernahme = data.decode('utf-8') # Decode the received message - grouprec = int(übernahme.split(": ")[0]) # Extract group ID, UUID, and IP address from the message - erhaltene_ip = (übernahme.split(": ")[2]) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - send_acknowledgement() # Send acknowledgment back to the sender - accept(grouprec,erhaltene_uuid2,erhaltene_ip) # Process the election or new leader message -########################### End - Leader Election ########################### - -########################### Start - Process client messages ########################### -def process_hold_back_queue(): - """ - Processes messages in the hold-back queue in the correct order. - """ - while hold_back_queue: - message = hold_back_queue.popleft() # Remove the oldest message from the queue - message_id, decoded_message = message - if message_id not in processed_message_ids: - # Process or forward the message - print(f"Processing message: {decoded_message}") - broadcast(decoded_message) # Forward the message to all participants - processed_message_ids.add(message_id) # Mark the message as processed - -def broadcast(message): - """ - Sends a message to all participants in the network via broadcast. - """ - server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - full_message = f"{message}".encode() # Encode the message - server_socket2.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - server_socket2.close() - -def listen_client(): - """ - Listens for messages from clients, processes them, and broadcasts them to other participants. - """ - global last_five_messages - server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_socket.bind(('', client_broadcast_port)) # Bind to the broadcast port - while True: # Wait to receive a message from a client - try: - message, client_address = server_socket.recvfrom(4096) # Receive a message from a client - decoded_message = message.decode() # Decode the message - last_five_messages.append(decoded_message) # Store the message for Leader heartbeat - #print(f"Message stored: {last_three_messages}") # Only for debugging - message_id = decoded_message.split(":")[0] # Extract the unique message ID (UUID) from the decoded message - if message_id in processed_message_ids: # Check if the message has already been processed - continue # Skip if the message was already processed - hold_back_queue.append((message_id, decoded_message)) # Add the message to the hold-back queue - process_hold_back_queue() # Process messages in the hold-back queue - except socket.error as e: # Handle socket errors - print(f"An error occurred while listening: {e}") - break - except KeyboardInterrupt: # Handle server shutdown via keyboard interrupt ????? Funktioniert das?? - print("\nShutting down server...") - break -########################### End - Process client messages ########################### - -########################### Start - Leader Heartbeat ################################# -def forward_received_heartbeat_to_clients(): - """ - Forwards the last 5 received heartbeat messages separately to clients via UDP broadcast. - """ - server_socket3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket3.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - print("Starting to send the messages from the heartbeat.") - for message in received_heartbeat: - try: - print(f"Forwarding message: {message}") # Print the message being forwarded for debugging - full_message = f"{message}".encode() # Encode the message - server_socket3.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - except Exception as e: - print(f"Error forwarding message: {e}") - server_socket3.close() # Close the socket to free up resources - -def listen_heartbeat(): - """ - Listens for the leader's heartbeat and forwards it to the right neighbor in the ring. - """ - global received_heartbeat - global last_heartbeat_time - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening - while True: # Receive data from the socket - data, addr = sock.recvfrom(1024) # Receive data from the socket - print("Leader heartbeat received.") - send_acknowledgement() # Send acknowledgment for the received message - received_heartbeat = pickle.loads(data) # Deserialize the received data - print(received_heartbeat) # Debugging. Print the received messages - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - if leader_ip == ip_address: # If the server is the leader - if send_heartbeat==received_heartbeat: - print(f"My heartbeat has traveled through the ring.") - else: - print(f"Received a foreign heartbeat. I am forwarding the messages.") - forward_received_heartbeat_to_clients() - else: # Forward the heartbeat to the right neighbor - print("Heartbeat wird an den nachbarn gesendet") - right_neighbour = get_neighbour(members_IP, ip_address, 'right') - msg = pickle.dumps(received_heartbeat) - try: - sock_heart = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending Hearbeat - sock_heart.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock_heart.sendto(msg, (right_neighbour , heartbeat_port)) # Send heartbeat to the right neighbor - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - sock_heart.close() # Close the socket to free up resources - except Exception as e: # Handle errors during data transmission - print(f"Error sending data lsiten heartbeat: {e}") - - -def leader_send_heartbeat(): - """ - Sends the Heartbeat to the next server in the ring. - """ - global send_heartbeat - global last_heartbeat_time - while True: - send_heartbeat = last_five_messages # Set the send_heartbeat as the last five messages - msg = pickle.dumps(send_heartbeat) # Serialize the messages - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) == 1: # If only this server is part of the Ring it does not have to send an Heartbeat - print("I am the only server. There is no other Server to send a heartbeat to.") - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - else: # Send the heartbeat to the right neighbor - try: - sock_heart_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - sock_heart_send.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock_heart_send.sendto(msg, (right_neighbour , heartbeat_port)) # Send the members list to the right neighbor - sock_heart_send.close() - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data Leader_send_heartbeat: {e}") - #sock_heart_send.close() # Close the socket to free up resources - time.sleep(5) # Wait before sending the next heartbeat - -def monitor_heartbeat(): ################ Achtung Testen, ob auch der Leader in ein timeout kommen kann... ggf. vor removal f leader IP einfügen####### - """ - Monitors whether the last heartbeat was received within the timeout period. - """ - global members_IP - global leader_ip - global last_heartbeat_time - while True: - time_since_last_heartbeat = time.time() - last_heartbeat_time # Calculate the time since the last heartbeat - if time_since_last_heartbeat > 18: - print(f"ERROR: No heartbeat received for 18 seconds! Leader is down.") - members_IP.remove(leader_ip) # Remove the Leader server from the member list - if len(members_IP) == 1: # If only one server remains, it becomes the leader - print("I am now the last server in this ring and therefore the leader.") - leader_ip = ip_address # Update the leader IP to the current server - is_leader.set_value(True) - else: - send_update_to_ring() # Update the ring and forward necessary messages - start_election() # Start leader election to determine new Leader - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - time.sleep(1) # Check for heartbeat status every second -########################### End - Leader Heartbeat ############################ - -########################### Start - Client Heartbeat ########################### -def leader_send_Client_heartbeat(): - """ - Broadcasts a heartbeat message to the clients, indicating the server is available. - """ - while True: - msg = ("Server is available.") # Define the heartbeat message - print("I am sending a heartbeat to the client.") # Debugging message indicating that the server is sending a heartbeat - server_client_heartbeat = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_client_heartbeat.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_client_heartbeat.sendto(msg.encode('utf-8'), (broadcast_ip, heartbeat_client_broadcast_port)) # # Send the broadcast message to the specified broadcast IP and port - server_client_heartbeat.close()# Close the socket to free up resources - time.sleep(5) # Wait for 15 seconds before sending the next heartbeat -########################### End - Client Heartbeat ########################### - -########################### Start - observation value changes leader ########################### -class VariableWatcher: # A utility class designed to monitor a variable's value and notify registered observers whenever the value changes. - def __init__(self): - self._value = None # Initializes the variable's value as None - self.observers = [] # Creates a list to store observers - def set_value(self, new_value): # Updates the variable and notifies observers. - self._value = new_value # Updates the variable's value - self.notify(new_value) # Notifies all observers about the new value - def add_observer(self, observer): - self.observers.append(observer) # Adds an observer to the list - def notify(self, new_value): - for observer in self.observers: # Iterates through all registered observers - observer(new_value) # Calls each observer with the new value - -def callback(new_value): - """ - This function is triggered when the `is_leader` value changes. - """ - if new_value: - print("I am now taking over leader responsibilities.") - thread7 = threading.Thread(target=listen_client) # Listens to client messages - thread8 = threading.Thread(target=new_server_in_ring) # Listens for election messages - thread9 = threading.Thread(target= leader_send_Client_heartbeat) # Start sending heartbeats to the client - thread10 = threading.Thread(target= leader_send_heartbeat) - thread7.start() - thread8.start() - thread9.start() - if len(received_heartbeat) > 0: # Check if there are values in received_heartbeat - print("I am sending the last 5 messages of the leader heartbeat to the clients.") - forward_received_heartbeat_to_clients() # Forward the heartbeat to the clients. - thread10.start() # Leader starts sending its heartbeat to its right neighbour. - else: - print("I am no longer the leader.") -########################### End - observation value changes leader ########################### - -####################################################################### - -if __name__ == "__main__": - # Create threads for different server operations - thread1 = threading.Thread(target=server_enters) # Handles server entry to the ring - thread2 = threading.Thread(target=lausche_update_Ring) # Listens for ring updates - thread4 = threading.Thread(target=zuhören_election) # Listens for election messages - thread5 = threading.Thread(target=listen_heartbeat) # Listens for leader heartbeat - thread6 = threading.Thread(target=monitor_heartbeat) # Monitord the leader heartbeat - # Start all threads - thread1.start() - thread2.start() - thread4.start() - thread5.start() - thread6.start() - - is_leader = VariableWatcher() # Create an instance of VariableWatcher to observe changes in leader status - is_leader.add_observer(callback) # Add the callback function as an observer for changes in `is_leader` \ No newline at end of file diff --git a/2025-01-10_Client_V6.py b/Client_25-01-25.py similarity index 98% rename from 2025-01-10_Client_V6.py rename to Client_25-01-25.py index fd451b48b1dd08d90bf7e416f152e9f8475e206a..36a35e9448d8a47093c3cdabc8316d563df2d73d 100644 --- a/2025-01-10_Client_V6.py +++ b/Client_25-01-25.py @@ -70,7 +70,7 @@ def sender(): full_message = f"{message_id}:{nickname}: {message}".encode() # Format the message with the user's nickname and content client_socket.sendto(full_message, (broadcast_ip, broadcast_port)) # Send the message to the broadcast address processed_message_ids.add(message_id) # Mark the message ID as processed - except KeyboardInterrupt: ######################whelp....................... Funktioniert das??? + except KeyboardInterrupt: #does not work # Handle when the user presses Ctrl+C print(f"\n{nickname} left the chat.") # Notify others that this client left the chat diff --git a/README.md b/README.md deleted file mode 100644 index 45f25d423890c559ccfbf39de036614487664d0c..0000000000000000000000000000000000000000 --- a/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# Distributed-Systems - - - -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://gitlab.reutlingen-university.de/wikatg24/distributed-systems.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://gitlab.reutlingen-university.de/wikatg24/distributed-systems/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README - -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/Server_127.0.0.1_Startet_Election.py b/Server_127.0.0.1_Startet_Election.py deleted file mode 100644 index e1fe8d02acdf27f360a8e68873633d2c148744e2..0000000000000000000000000000000000000000 --- a/Server_127.0.0.1_Startet_Election.py +++ /dev/null @@ -1,117 +0,0 @@ - -#Test Communication UDP between 2 -import socket -from uuid import uuid4 -import uuid - -ELECTION = 0 -NEW_LEAD = 1 -my_ip = '127.0.0.1' -election_socket = 12345 -myuuid = uuid.uuid4() #UUid auch als String vergleichbar? -participating = False -is_leader = False -Leader = False -print(myuuid) - -#Nachbar des Servers im Ring wird ermittelt - an diesen wird später die Election Message gesendet Achtung wieso ist beim letzenm der partner nicht der erste?? -def get_neighbour(members, current_member_ip, direction='left'): - current_member_index = members.index(current_member_ip) if current_member_ip in members else -1 - if current_member_index != -1: - if direction == 'left': - if current_member_index + 1 == len(members): - return members[0] - else: - return members[current_member_index + 1] - else: - if current_member_index - 1 < 0: - return members[len(members) - 1] - else: - return members[current_member_index - 1] - else: - return None - -members = ['127.0.0.1', '127.0.0.2', '127.0.0.5'] -neighbour = get_neighbour(members, my_ip , 'right') -print(neighbour) - -#Funktion muss aufgerufen werden! -def start_election(): - print("{} is starting an election.".format(myuuid)) - #Server nimmt an Election Teil - participating = True - #Nachricht wird an den Server neben an gesendet - msg = f"{ELECTION}: {myuuid}".encode('utf-8') - #Msg encoden? Variable setzen? - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(neighbour,election_socket)) - #data,addr=send_socket.recvfrom(4096) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - - - -def accept(group,erhaltene_uuid): - if group == ELECTION: - #Abgleich der erhaltenen Uuid mit der eigenen uuid - if erhaltene_uuid > myuuid: - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}".encode() - #Weitergabe der höheren uuid - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(neighbour,election_socket)) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - if erhaltene_uuid < myuuid: - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}".encode() - #Weitergabe der höheren uuid - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(neighbour,election_socket)) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - if erhaltene_uuid == myuuid: - print("{} starts acting as a leader!".format(myuuid)) - participating = False - is_leader = True - leader = myuuid - msg = f"{NEW_LEAD}: {myuuid}".encode() - #Weitergabe der höheren uuid - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(neighbour,election_socket)) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - if group == NEW_LEAD: - if erhaltene_uuid == myuuid: - return - if erhaltene_uuid != myuuid: - print("{} acknowledged new leader.".format(myuuid)) - leader = myuuid - msg = f"{NEW_LEAD}: {erhaltene_uuid}".encode() - #Weitergabe der höheren uuid - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.sendto(msg,(neighbour,election_socket)) - #Was passiert wenn diese Nachricht nicht ankommt? - send_socket.close() - - -#zuhören -start_election() -sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) -sock.bind((my_ip,election_socket)) -while True: - data,addr=sock.recvfrom(4096) - übernahme = data.decode('utf-8') - #message = ("Hello i am server").encode('utf-8') - #Für Piggiback Acn nutzen? - #sock.sendto(message,addr) - grouprec = int(übernahme.split(": ")[0]) - #print(übernahme) - #print((übernahme.split(": ")[1])) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - accept(grouprec,erhaltene_uuid2) - - - diff --git a/2025-01-10_Server_V11_mit_Schleife_Eintritt_Server.py b/Server_25-01-25.py similarity index 97% rename from 2025-01-10_Server_V11_mit_Schleife_Eintritt_Server.py rename to Server_25-01-25.py index 0ab48f58a1399e5809fd7566a55e45bc344a9f3f..78e9c4e21284ae3f13b80208947cc6c4f337943a 100644 --- a/2025-01-10_Server_V11_mit_Schleife_Eintritt_Server.py +++ b/Server_25-01-25.py @@ -1,584 +1,584 @@ - -from inspect import _empty -import time -import threading -from uuid import uuid4 -import socket -import uuid -import pickle -import json -import multiprocessing -import os -from multiprocessing import Manager -from collections import deque - -# Global variables to manage ring members and their information -global members_UUID -global members_IP -global last_heartbeat_time -# Initialize lists to keep track of members -members_UUID = [] # List for UUIDs of members in the ring -members_IP = [] # List for IP addresses of members in the ring -# Network and port configurations -broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network -enter_port = 12348 #Port that is used for the discovery of new server participants -ringport = 12343 -election_port = 12345 -client_broadcast_port = 55555 #client sends, server listens -client_broadcast_port2 = 33333 #server sends, client listens -acknowledgement_port = 22222 -heartbeat_port = 44444 -heartbeat_client_broadcast_port = 11111 -# Unique identification for this server -myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 -my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 -hostname = socket.gethostname() -ip_address = socket.gethostbyname(hostname) # Retrieves the hostname of the current machine -#ip_address = "127.0.0.1" ########verwenden zum Testen auf einem Gerät -# Leader election-related variables -participating = False # Indicates whether the server is currently participating in an election -is_leader = False # Boolean flag to indicate if the server is the leader -Leader = False # Alternate flag to indicate leader status (can be consolidated with is_leader) -ELECTION = 0 # Message type for initiating an election -NEW_LEAD = 1 # Message type for announcing a new leader -leader_ip = 'unknown' # Stores the IP address of the current leader; default is 'unknown' -# Heartbeat related variables -last_three_messages = deque(maxlen=3) # Initialization of message storage (last 3 messages) -send_heartbeat = deque() -received_heartbeat = deque() -last_heartbeat_time = time.time() - -# variables for Listen to Client -hold_back_queue = deque() # A double-ended queue (deque) to store messages that need to be temporarily held back before they are processed. -processed_message_ids = set() # A set to track the IDs of messages that have already been processed. This helps avoid duplicate processing. - -########################### Start - Neighbour ########################### -def get_neighbour(members_IP, current_member_ip, direction='left'): - """ - Determines the neighbor of a server in a circular ring topology based on the direction. - """ - current_member_index = members_IP.index(current_member_ip) if current_member_ip in members_IP else -1 # Find the index of the current member in the list. If not found, set to -1. - if current_member_index != -1: # Determine the neighbor to the 'left' - if direction == 'left': # Determine the neighbor to the 'left' (next in the ring) - if current_member_index + 1 == len(members_IP): # If the current member is the last in the list, wrap around to the first - return members_IP[0] # Return the first member in the list - else: - return members_IP[current_member_index + 1] # Return the next member in the list - else: # Determine the neighbor to the 'right' - if current_member_index - 1 < 0: # If the current member is the first in the list, wrap around to the last - return members_IP[len(members_IP) - 1] # Return the last member in the list - else: - return members_IP[current_member_index - 1] # Return the previous member in the list - else: - return None # If the current member IP is not found in the list, return None -########################### End - Neighbour ########################### - -########################### Start - Acknowledgement ############################ -def send_acknowledgement(): - """ - Function for sending an acknowledgment for a received message. - """ - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'left') # Determine the right neighbor based on the current ring structure - msg=("Your Message was received.") # Message sent as a receipt confirmation - send_ack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket to send the acknowledgment - send_ack_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_ack_socket.sendto(msg.encode('utf-8'),(right_neighbour,acknowledgement_port)) # Send the acknowledgment message to the right neighbor using the acknowledgement_port - send_ack_socket.close() # Close the socket after sending the message - -def receive_acknowledgement(msg, ip, so): - """ - Function for receiving acknowledgments. This section includes the system's response to messages not received between servers. - """ - global members_IP - global is_leader - global leader_ip - - if len(members_IP) > 1: - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - recack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to receive acknowledgment messages - recack_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - recack_socket.bind((ip_address,acknowledgement_port)) - recack_socket.settimeout(2) # Set a timeout for receiving acknowledgment --> Notwendig in welcher Höhe???? Testen?? - try: - data, addr = recack_socket.recvfrom(1024) # Attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: - print(f"No response received. Retrying message delivery.") # If no response is received, resend the message - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Open a temporary socket for resending the original message - temp_send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - temp_send_socket.sendto(msg,(ip,so)) - temp_send_socket.close() - recack_socket.settimeout(2) # Set a new timeout for acknowledgment - try: - data, addr = recack_socket.recvfrom(1024) # Second attempt to receive a response - print(f"{addr} has received the message.") - recack_socket.close() - except socket.timeout: # If the second attempt fails, handle server failure - print(f"No response again. Server is unreachable. Triggering ring update.") - recack_socket.close() - members_IP.remove(right_neighbour) # Remove the failed server from the member list - # Check if the failed server was the leader - if len(members_IP) == 1: - # If only one server remains, it becomes the leader - if leader_ip != ip_address: # Check if the leader IP is its own; only change is_leader to True if the server was not the leader before - leader_ip = ip_address # Set leder_ip to own IP - is_leader.set_value(True) - print("I am now the last server in this ring and therefore the leader.") - else: - # Update the ring and forward necessary messages - send_update_to_ring() - if right_neighbour == leader_ip: #check if failed server was the leader - start_election() - new_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the new right neighbor with updated ring - temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - temp_send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - temp_send_socket.sendto(msg,(new_neighbour,so)) # Forward the original message to the new neighbor - temp_send_socket.close() - receive_acknowledgement(msg, new_neighbour, so) # Wait for acknowledgment from the new neighbor -########################### End - Acknowledgement ########################### - -########################### Start - Update ring ########################### -def lausche_update_Ring(): - """ - Listens for ring updates via UDP broadcasts and handles updates to the members_IP list. - """ - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for communication - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock.bind((ip_address, ringport)) # Bind the socket to the servers IP address and ring port for listening - while True: - try: - data, addr = sock.recvfrom(1024) # Receive data from the socket - members_IP2 = json.loads(data.decode()) # Decode the received JSON data to update the members list - if members_IP2 == members_IP: # Check if the received members list matches the current members list - print(f"Ring update has traveled through the ring.") - send_acknowledgement() # Send an acknowledgment for the received update - else: - members_IP = members_IP2 # Update the local members list - print(f"Ring update received: {members_IP}") - send_acknowledgement() # Send an acknowledgment for the received update - send_update_to_ring() # Forward the updated member list to the next neighbor - except json.JSONDecodeError: - print("Error decoding the JSON data.") # Handle errors in decoding the JSON data - -def send_update_to_ring(): - """ - Sends the updated members list to the next server in the ring. - """ - global members_IP - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) > 1:# If no right neighbor exists, there is no one to send the update to - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - data = json.dumps(members_IP).encode() # Serialize the members list into JSON format - sock.sendto(data, (right_neighbour , ringport)) # Send the members list to the right neighbor - sock.close()# Close the socket to free up resources - receive_acknowledgement(data, right_neighbour , ringport) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data to Ring: {e}") -########################### End - Update ring ########################### - -########################### Start - Server Enters ########################### -def new_server_in_ring(): - """ - This function is executed by the Leader. It listens for incoming messages from servers attempting to join the network. - The Leader maintains the list of all IP addresses in the ring and updates the topology whenever a new server joins. - Topology Updates: The Leader ensures all servers in the network are aware of the latest ring structure. - """ - global members_UUID - global members_IP - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.bind(("0.0.0.0", enter_port)) # Bind the socket to the address "0.0.0.0" and the enter_port - print("Server is running and waiting for broadcast messages from new servers.") - while True: - data, addr = sock.recvfrom(1024) # Listen for messages from other servers - print(f"Message received from {addr}: {data.decode()}") - new_server_ip, new_server_port = addr # Extract the IP and port of the new server from the received address - new_IP = data.decode() - new_IP = new_IP.split(": ")[1] # Extract the IP address from the message - if new_IP in members_IP: # Check if the IP already exists. This might happen if a server temporarily lost connection. #######Tritt das überhaupt ein? In Gruppe diskutieren - f"There are already servers. I am your leader: {ip_address}".encode() # Create a message for the new server# If the server already exists, send the Welcome Message - else: - members_IP.append(new_IP) # If the server is new, add its IP to the list - msg = f"There are already servers. I am your leader: {ip_address}".encode() # Create a message for the new server - #AttributeError: 'bytes' object has no attribute 'encode'. Did you mean: 'decode'? - sock.sendto(msg, (new_IP, new_server_port)) # Send the greeting message back to the new server - print(f"The updated IP_Ring is: {members_IP}") - send_update_to_ring() # Update the ring topology - -def server_enters(): - """ - This function is used when a server wants to join the network. It sends a greeting message to the broadcast address and waits for a response from the Leader. - If no response is received, the server assumes the Leader role and starts managing the ring itself. - Broadcast Communication: This allows new servers to discover the Leader without knowing its specific IP address. - """ - global members_UUID - global leader_ip - max_retries = 3 - success = False - intervall = 2 #time to wait fpr a response - msg = f"I am new: {ip_address}".encode() # Greeting message from the new server - - for attempt in range(max_retries): - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode - sock.settimeout(intervall) # Set a timeout to wait for a response --> ggf. anpassen, je nach geschwindigkeit mit Handy Internet - print(f"Sending discovery message (attempt{attempt+1}/{max_retries})") - sock.sendto(msg, (broadcast_ip, enter_port)) # Send the greeting message to the broadcast address using the enter_port - try: - while True: - data, addr = sock.recvfrom(1024) # receiving response - print(f"Answer from {addr}: {data.decode()}") - success = True - #sock.close() - my_leader = data.decode().split(": ")[1] # Extract the Leader's IP address from the response - leader_ip = my_leader # Set leder_ip to the received IP - except socket.timeout: - print(f"No response received for attempt {attempt+1}.") - if success: - print("Response received. Stopping retries.") - - break - if not success: - print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") # If no answer is received the server sets itself as leader - members_UUID.append(my_ID) # Add itself as a participant in the ring - members_IP.append(ip_address) # Add itself as a participant in the ring - sock.close() - leader_ip = ip_address # Set leder_ip to own IP - print(leader_ip) - is_leader.set_value(True) # Mark itself as the Leader - sock.close() -########################### End - Server Enters ########################### - -########################### Start - Leader Election ########################### -def start_election(): - """ - Initiates an election by sending the server's UUID and IP address to its right neighbor in the ring. - Marks the server as participating in the election process and waits for acknowledgment. - """ - global participating - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - print("{} is starting an election.".format(myuuid)) - participating = True # Server marks itself as participating in the election - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode('utf-8') # Create the election message with the server's UUID and IP address - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to send the election message - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send msg to the right neighbor usinf the election_port - send_socket.close() - receive_acknowledgement(msg, right_neighbour, election_port) # Wait for acknowledgment - -def accept(group,erhaltene_uuid,erhaltene_ip): - """ - Function to handle election messages and determine the next steps. - If the message is part of an election (ELECTION), the server compares UUIDs to either forward, update, or declare itself as the leader. - If the message is a new leader announcement (NEW_LEAD), it updates the local leader information and forwards the message. - Leader Election: Based on comparing UUIDs, the server with the highest UUID becomes the leader. - Acknowledgment Mechanism: Ensures that messages are received and processed reliably. - """ - global leader_ip - global is_leader - global last_heartbeat_time - global participating - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if group == ELECTION: # If the received message is part of an election - # Compare the received UUID with the server's own UUID - if erhaltene_uuid > myuuid: # Received UUID is greater, so forward the message without changes - print("{} is forwarding without updates.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - elif erhaltene_uuid < myuuid and participating==False: # Received UUID is smaller, update with the server's own UUID and forward - print("{} is updating and forwarding.".format(myuuid)) - participating = True - msg = f"{ELECTION}: {myuuid}: {ip_address}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - if erhaltene_uuid == myuuid: # If the server receives its own UUID, it becomes the leader - print("{} starts acting as a leader!".format(myuuid)) - participating = False - leader_ip = ip_address # Set leader_ip to own IP - leader = myuuid #Set leader to own uuid - msg = f"{NEW_LEAD}: {myuuid}: {ip_address}".encode() - #if leader_ip != ip_address: # Update leadership status if server was not already the leader bevor the election - is_leader.set_value(True) - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - - if group == NEW_LEAD: # If the received message announces a new leader - if erhaltene_uuid == myuuid: # If the UUID matches, the server has already acknowledged - return - if erhaltene_uuid != myuuid: # Update the leader information and forward the new leader announcement - print("{} acknowledged new leader.".format(myuuid)) - if leader_ip == ip_address: # Check if this server was the Leader bevor the election and set is_leader to False - is_leader.set_value(False) - leader_ip = erhaltene_ip # Update leader_ip - leader = erhaltene_uuid # Update leader - msg = f"{NEW_LEAD}: {erhaltene_uuid}: {erhaltene_ip}".encode() - send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) - send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour - send_socket.close() - receive_acknowledgement(msg, right_neighbour , election_port) - last_heartbeat_time = time.time() - -def zuhören_election(): - """ - Listens for incoming election or leader messages on the configured election socket. - Decodes the message, sends an acknowledgment to the sender, and processes the message via the accept() function. - """ - #sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - #sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - #sock.bind((ip_address,election_port)) # Bind to the election socket - while True: - sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock.bind((ip_address,election_port)) - data,addr=sock.recvfrom(4096) # Receive data from other servers - übernahme = data.decode('utf-8') # Decode the received message - grouprec = int(übernahme.split(": ")[0]) # Extract group ID, UUID, and IP address from the message - erhaltene_ip = (übernahme.split(": ")[2]) - erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) - sock.close() - send_acknowledgement() # Send acknowledgment back to the sender - accept(grouprec,erhaltene_uuid2,erhaltene_ip) # Process the election or new leader message -########################### End - Leader Election ########################### - -########################### Start - Process client messages ########################### -def process_hold_back_queue(): - """ - Processes messages in the hold-back queue in the correct order. - """ - while hold_back_queue: - message = hold_back_queue.popleft() # Remove the oldest message from the queue - message_id, decoded_message = message - if message_id not in processed_message_ids: - # Process or forward the message - print(f"Processing message: {decoded_message}") - broadcast(decoded_message) # Forward the message to all participants - processed_message_ids.add(message_id) # Mark the message as processed - -def broadcast(message): - """ - Sends a message to all participants in the network via broadcast. - """ - server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - full_message = f"{message}".encode() # Encode the message - server_socket2.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - server_socket2.close() - -def listen_client(): - """ - Listens for messages from clients, processes them, and broadcasts them to other participants. - """ - global last_three_messages - server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_socket.bind(('', client_broadcast_port)) # Bind to the broadcast port - while True: # Wait to receive a message from a client - try: - message, client_address = server_socket.recvfrom(4096) # Receive a message from a client - decoded_message = message.decode() # Decode the message - last_three_messages.append(decoded_message) # Store the message for Leader heartbeat - #print(f"Message stored: {last_three_messages}") # Only for debugging - message_id = decoded_message.split(":")[0] # Extract the unique message ID (UUID) from the decoded message - if message_id in processed_message_ids: # Check if the message has already been processed - continue # Skip if the message was already processed - hold_back_queue.append((message_id, decoded_message)) # Add the message to the hold-back queue - process_hold_back_queue() # Process messages in the hold-back queue - except socket.error as e: # Handle socket errors - print(f"An error occurred while listening: {e}") - break - except KeyboardInterrupt: # Handle server shutdown via keyboard interrupt ????? Funktioniert das?? - print("\nShutting down server...") - break -########################### End - Process client messages ########################### - -########################### Start - Leader Heartbeat ################################# -def forward_received_heartbeat_to_clients(): - """ - Forwards the last 3 received heartbeat messages separately to clients via UDP broadcast. - """ - server_socket3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_socket3.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - print("Starting to send the messages from the heartbeat.") - for message in received_heartbeat: - try: - print(f"Forwarding message: {message}") # Print the message being forwarded for debugging - full_message = f"{message}".encode() # Encode the message - server_socket3.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message - except Exception as e: - print(f"Error forwarding message: {e}") - server_socket3.close() # Close the socket to free up resources - -def listen_heartbeat(): - """ - Listens for the leader's heartbeat and forwards it to the right neighbor in the ring. - """ - global received_heartbeat - global last_heartbeat_time - #sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats - #sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - #sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening - while True: # Receive data from the socket - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening - data, addr = sock.recvfrom(1024) # Receive data from the socket - print("Leader heartbeat received.") - sock.close() - send_acknowledgement() # Send acknowledgment for the received message - received_heartbeat = pickle.loads(data) # Deserialize the received data - #print(received_heartbeat) # Debugging. Print the received messages - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - if leader_ip == ip_address: # If the server is the leader - if send_heartbeat==received_heartbeat: - print(f"My heartbeat has traveled through the ring.") - else: - print(f"Received a foreign heartbeat. I am forwarding the messages.") - forward_received_heartbeat_to_clients() - else: # Forward the heartbeat to the right neighbor - print("Heartbeat is forwarded to the neigbbor.") - right_neighbour = get_neighbour(members_IP, ip_address, 'right') - msg = pickle.dumps(received_heartbeat) - try: - sock_heart = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending Hearbeat - sock_heart.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock_heart.sendto(msg, (right_neighbour , heartbeat_port)) # Send heartbeat to the right neighbor - sock_heart.close() # Close the socket to free up resources - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - - except Exception as e: # Handle errors during data transmission - print(f"Error sending data lsiten heartbeat: {e}") - - -def leader_send_heartbeat(): - """ - Sends the Heartbeat to the next server in the ring. - """ - global send_heartbeat - global last_heartbeat_time - while True: - send_heartbeat = last_three_messages # Set the send_heartbeat as the last three messages - msg = pickle.dumps(send_heartbeat) # Serialize the messages - right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure - if len(members_IP) == 1: # If only this server is part of the Ring it does not have to send an Heartbeat - print("I am the only server. There is no other Server to send a heartbeat to.") - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - else: # Send the heartbeat to the right neighbor - try: - sock_heart_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates - sock_heart_send.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) - sock_heart_send.sendto(msg, (right_neighbour , heartbeat_port)) # Send the members list to the right neighbor - sock_heart_send.close() - receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor - except Exception as e: # Handle errors during data transmission - print(f"Error sending data Leader_send_heartbeat: {e}") - sock_heart_send.close() # Close the socket to free up resources - time.sleep(5) # Wait before sending the next heartbeat - -def monitor_heartbeat(): ################ Achtung Testen, ob auch der Leader in ein timeout kommen kann... ggf. vor removal f leader IP einfügen####### - """ - Monitors whether the last heartbeat was received within the timeout period. - """ - global members_IP - global leader_ip - global last_heartbeat_time - while True: - time_since_last_heartbeat = time.time() - last_heartbeat_time # Calculate the time since the last heartbeat - if time_since_last_heartbeat > 11: - print(f"ERROR: No heartbeat received for 18 seconds! Leader is down.") - members_IP.remove(leader_ip) # Remove the Leader server from the member list - if len(members_IP) == 1: # If only one server remains, it becomes the leader - print("I am now the last server in this ring and therefore the leader.") - leader_ip = ip_address # Update the leader IP to the current server - is_leader.set_value(True) - else: - send_update_to_ring() # Update the ring and forward necessary messages - start_election() # Start leader election to determine new Leader - last_heartbeat_time = time.time() # Update the last heartbeat timestamp - time.sleep(1) # Check for heartbeat status every second -########################### End - Leader Heartbeat ############################ - -########################### Start - Client Heartbeat ########################### -def leader_send_Client_heartbeat(): - """ - Broadcasts a heartbeat message to the clients, indicating the server is available. - """ - while True: - msg = ("Server is available.") # Define the heartbeat message - print("I am sending a heartbeat to the client.") # Debugging message indicating that the server is sending a heartbeat - server_client_heartbeat = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket - server_client_heartbeat.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode - server_client_heartbeat.sendto(msg.encode('utf-8'), (broadcast_ip, heartbeat_client_broadcast_port)) # # Send the broadcast message to the specified broadcast IP and port - server_client_heartbeat.close()# Close the socket to free up resources - time.sleep(2) # Wait for 15 seconds before sending the next heartbeat -########################### End - Client Heartbeat ########################### - -########################### Start - observation value changes leader ########################### -class VariableWatcher: # A utility class designed to monitor a variable's value and notify registered observers whenever the value changes. - def __init__(self): - self._value = None # Initializes the variable's value as None - self.observers = [] # Creates a list to store observers - def set_value(self, new_value): # Updates the variable and notifies observers. - self._value = new_value # Updates the variable's value - self.notify(new_value) # Notifies all observers about the new value - def add_observer(self, observer): - self.observers.append(observer) # Adds an observer to the list - def notify(self, new_value): - for observer in self.observers: # Iterates through all registered observers - observer(new_value) # Calls each observer with the new value - -def callback(new_value): - """ - This function is triggered when the `is_leader` value changes. - """ - if new_value: - print("I am now taking over leader responsibilities.") - thread7 = threading.Thread(target=listen_client) # Listens to client messages - thread8 = threading.Thread(target=new_server_in_ring) # Listens for election messages - thread9 = threading.Thread(target= leader_send_Client_heartbeat) # Start sending heartbeats to the client - thread10 = threading.Thread(target= leader_send_heartbeat) - thread7.start() - thread8.start() - thread9.start() - if len(received_heartbeat) > 0: # Check if there are values in received_heartbeat - print("I am sending the last 3 messages of the leader heartbeat to the clients.") - forward_received_heartbeat_to_clients() # Forward the heartbeat to the clients. - thread10.start() # Leader starts sending its heartbeat to its right neighbour. - else: - print("I am no longer the leader.") -########################### End - observation value changes leader ########################### - -####################################################################### - -if __name__ == "__main__": - # Create threads for different server operations - thread1 = threading.Thread(target=server_enters) # Handles server entry to the ring - thread2 = threading.Thread(target=lausche_update_Ring) # Listens for ring updates - thread4 = threading.Thread(target=zuhören_election) # Listens for election messages - thread5 = threading.Thread(target=listen_heartbeat) # Listens for leader heartbeat - thread6 = threading.Thread(target=monitor_heartbeat) # Monitord the leader heartbeat - # Start all threads - thread1.start() - thread2.start() - thread4.start() - thread5.start() - thread6.start() - - is_leader = VariableWatcher() # Create an instance of VariableWatcher to observe changes in leader status + +from inspect import _empty +import time +import threading +from uuid import uuid4 +import socket +import uuid +import pickle +import json +import multiprocessing +import os +from multiprocessing import Manager +from collections import deque + +# Global variables to manage ring members and their information +global members_UUID +global members_IP +global last_heartbeat_time +# Initialize lists to keep track of members +members_UUID = [] # List for UUIDs of members in the ring +members_IP = [] # List for IP addresses of members in the ring +# Network and port configurations +broadcast_ip = "255.255.255.255" #Broadcast-adress in the Network +enter_port = 12348 #Port that is used for the discovery of new server participants +ringport = 12343 +election_port = 12345 +client_broadcast_port = 55555 #client sends, server listens +client_broadcast_port2 = 33333 #server sends, client listens +acknowledgement_port = 22222 +heartbeat_port = 44444 +heartbeat_client_broadcast_port = 11111 +# Unique identification for this server +myuuid = uuid.uuid4() #Creating a unique ip Adress using uuid4 +my_ID = str(myuuid) #Creating a unique ip Adress using uuid4 +hostname = socket.gethostname() +ip_address = socket.gethostbyname(hostname) # Retrieves the hostname of the current machine +#ip_address = "127.0.0.1" ########verwenden zum Testen auf einem Gerät +# Leader election-related variables +participating = False # Indicates whether the server is currently participating in an election +is_leader = False # Boolean flag to indicate if the server is the leader +Leader = False # Alternate flag to indicate leader status (can be consolidated with is_leader) +ELECTION = 0 # Message type for initiating an election +NEW_LEAD = 1 # Message type for announcing a new leader +leader_ip = 'unknown' # Stores the IP address of the current leader; default is 'unknown' +# Heartbeat related variables +last_three_messages = deque(maxlen=3) # Initialization of message storage (last 3 messages) +send_heartbeat = deque() +received_heartbeat = deque() +last_heartbeat_time = time.time() + +# variables for Listen to Client +hold_back_queue = deque() # A double-ended queue (deque) to store messages that need to be temporarily held back before they are processed. +processed_message_ids = set() # A set to track the IDs of messages that have already been processed. This helps avoid duplicate processing. + +########################### Start - Neighbour ########################### +def get_neighbour(members_IP, current_member_ip, direction='left'): + """ + Determines the neighbor of a server in a circular ring topology based on the direction. + """ + current_member_index = members_IP.index(current_member_ip) if current_member_ip in members_IP else -1 # Find the index of the current member in the list. If not found, set to -1. + if current_member_index != -1: # Determine the neighbor to the 'left' + if direction == 'left': # Determine the neighbor to the 'left' (next in the ring) + if current_member_index + 1 == len(members_IP): # If the current member is the last in the list, wrap around to the first + return members_IP[0] # Return the first member in the list + else: + return members_IP[current_member_index + 1] # Return the next member in the list + else: # Determine the neighbor to the 'right' + if current_member_index - 1 < 0: # If the current member is the first in the list, wrap around to the last + return members_IP[len(members_IP) - 1] # Return the last member in the list + else: + return members_IP[current_member_index - 1] # Return the previous member in the list + else: + return None # If the current member IP is not found in the list, return None +########################### End - Neighbour ########################### + +########################### Start - Acknowledgement ############################ +def send_acknowledgement(): + """ + Function for sending an acknowledgment for a received message. + """ + if len(members_IP) > 1: + right_neighbour = get_neighbour(members_IP, ip_address, 'left') # Determine the right neighbor based on the current ring structure + msg=("Your Message was received.") # Message sent as a receipt confirmation + send_ack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a UDP socket to send the acknowledgment + send_ack_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + send_ack_socket.sendto(msg.encode('utf-8'),(right_neighbour,acknowledgement_port)) # Send the acknowledgment message to the right neighbor using the acknowledgement_port + send_ack_socket.close() # Close the socket after sending the message + +def receive_acknowledgement(msg, ip, so): + """ + Function for receiving acknowledgments. This section includes the system's response to messages not received between servers. + """ + global members_IP + global is_leader + global leader_ip + + if len(members_IP) > 1: + right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure + recack_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to receive acknowledgment messages + recack_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + recack_socket.bind((ip_address,acknowledgement_port)) + recack_socket.settimeout(2) # Set a timeout for receiving acknowledgment --> Notwendig in welcher Höhe???? Testen?? + try: + data, addr = recack_socket.recvfrom(1024) # Attempt to receive a response + print(f"{addr} has received the message.") + recack_socket.close() + except socket.timeout: + print(f"No response received. Retrying message delivery.") # If no response is received, resend the message + temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Open a temporary socket for resending the original message + temp_send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + temp_send_socket.sendto(msg,(ip,so)) + temp_send_socket.close() + recack_socket.settimeout(2) # Set a new timeout for acknowledgment + try: + data, addr = recack_socket.recvfrom(1024) # Second attempt to receive a response + print(f"{addr} has received the message.") + recack_socket.close() + except socket.timeout: # If the second attempt fails, handle server failure + print(f"No response again. Server is unreachable. Triggering ring update.") + recack_socket.close() + members_IP.remove(right_neighbour) # Remove the failed server from the member list + # Check if the failed server was the leader + if len(members_IP) == 1: + # If only one server remains, it becomes the leader + if leader_ip != ip_address: # Check if the leader IP is its own; only change is_leader to True if the server was not the leader before + leader_ip = ip_address # Set leder_ip to own IP + is_leader.set_value(True) + print("I am now the last server in this ring and therefore the leader.") + else: + # Update the ring and forward necessary messages + send_update_to_ring() + if right_neighbour == leader_ip: #check if failed server was the leader + start_election() + new_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the new right neighbor with updated ring + temp_send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) + temp_send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + temp_send_socket.sendto(msg,(new_neighbour,so)) # Forward the original message to the new neighbor + temp_send_socket.close() + receive_acknowledgement(msg, new_neighbour, so) # Wait for acknowledgment from the new neighbor +########################### End - Acknowledgement ########################### + +########################### Start - Update ring ########################### +def lausche_update_Ring(): + """ + Listens for ring updates via UDP broadcasts and handles updates to the members_IP list. + """ + global members_IP + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for communication + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + sock.bind((ip_address, ringport)) # Bind the socket to the servers IP address and ring port for listening + while True: + try: + data, addr = sock.recvfrom(1024) # Receive data from the socket + members_IP2 = json.loads(data.decode()) # Decode the received JSON data to update the members list + if members_IP2 == members_IP: # Check if the received members list matches the current members list + print(f"Ring update has traveled through the ring.") + send_acknowledgement() # Send an acknowledgment for the received update + else: + members_IP = members_IP2 # Update the local members list + print(f"Ring update received: {members_IP}") + send_acknowledgement() # Send an acknowledgment for the received update + send_update_to_ring() # Forward the updated member list to the next neighbor + except json.JSONDecodeError: + print("Error decoding the JSON data.") # Handle errors in decoding the JSON data + +def send_update_to_ring(): + """ + Sends the updated members list to the next server in the ring. + """ + global members_IP + right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure + if len(members_IP) > 1:# If no right neighbor exists, there is no one to send the update to + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + data = json.dumps(members_IP).encode() # Serialize the members list into JSON format + sock.sendto(data, (right_neighbour , ringport)) # Send the members list to the right neighbor + sock.close()# Close the socket to free up resources + receive_acknowledgement(data, right_neighbour , ringport) # Wait for acknowledgment from the right neighbor + except Exception as e: # Handle errors during data transmission + print(f"Error sending data to Ring: {e}") +########################### End - Update ring ########################### + +########################### Start - Server Enters ########################### +def new_server_in_ring(): + """ + This function is executed by the Leader. It listens for incoming messages from servers attempting to join the network. + The Leader maintains the list of all IP addresses in the ring and updates the topology whenever a new server joins. + Topology Updates: The Leader ensures all servers in the network are aware of the latest ring structure. + """ + global members_UUID + global members_IP + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket + sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode + sock.bind(("0.0.0.0", enter_port)) # Bind the socket to the address "0.0.0.0" and the enter_port + print("Server is running and waiting for broadcast messages from new servers.") + while True: + data, addr = sock.recvfrom(1024) # Listen for messages from other servers + print(f"Message received from {addr}: {data.decode()}") + new_server_ip, new_server_port = addr # Extract the IP and port of the new server from the received address + new_IP = data.decode() + new_IP = new_IP.split(": ")[1] # Extract the IP address from the message + if new_IP in members_IP: # Check if the IP already exists. This might happen if a server temporarily lost connection. #######Tritt das überhaupt ein? In Gruppe diskutieren + f"There are already servers. I am your leader: {ip_address}".encode() # Create a message for the new server# If the server already exists, send the Welcome Message + else: + members_IP.append(new_IP) # If the server is new, add its IP to the list + msg = f"There are already servers. I am your leader: {ip_address}".encode() # Create a message for the new server + #AttributeError: 'bytes' object has no attribute 'encode'. Did you mean: 'decode'? + sock.sendto(msg, (new_IP, new_server_port)) # Send the greeting message back to the new server + print(f"The updated IP_Ring is: {members_IP}") + send_update_to_ring() # Update the ring topology + +def server_enters(): + """ + This function is used when a server wants to join the network. It sends a greeting message to the broadcast address and waits for a response from the Leader. + If no response is received, the server assumes the Leader role and starts managing the ring itself. + Broadcast Communication: This allows new servers to discover the Leader without knowing its specific IP address. + """ + global members_UUID + global leader_ip + max_retries = 3 + success = False + intervall = 2 #time to wait fpr a response + msg = f"I am new: {ip_address}".encode() # Greeting message from the new server + + for attempt in range(max_retries): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket + sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Set the socket to broadcast mode + sock.settimeout(intervall) # Set a timeout to wait for a response --> ggf. anpassen, je nach geschwindigkeit mit Handy Internet + print(f"Sending discovery message (attempt{attempt+1}/{max_retries})") + sock.sendto(msg, (broadcast_ip, enter_port)) # Send the greeting message to the broadcast address using the enter_port + try: + while True: + data, addr = sock.recvfrom(1024) # receiving response + print(f"Answer from {addr}: {data.decode()}") + success = True + #sock.close() + my_leader = data.decode().split(": ")[1] # Extract the Leader's IP address from the response + leader_ip = my_leader # Set leder_ip to the received IP + except socket.timeout: + print(f"No response received for attempt {attempt+1}.") + if success: + print("Response received. Stopping retries.") + + break + if not success: + print(f"Keine Antwort erhalten. Ich bin jetzt der Leader. Meine IP: {ip_address}") # If no answer is received the server sets itself as leader + members_UUID.append(my_ID) # Add itself as a participant in the ring + members_IP.append(ip_address) # Add itself as a participant in the ring + sock.close() + leader_ip = ip_address # Set leder_ip to own IP + print(leader_ip) + is_leader.set_value(True) # Mark itself as the Leader + sock.close() +########################### End - Server Enters ########################### + +########################### Start - Leader Election ########################### +def start_election(): + """ + Initiates an election by sending the server's UUID and IP address to its right neighbor in the ring. + Marks the server as participating in the election process and waits for acknowledgment. + """ + global participating + right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure + print("{} is starting an election.".format(myuuid)) + participating = True # Server marks itself as participating in the election + msg = f"{ELECTION}: {myuuid}: {ip_address}".encode('utf-8') # Create the election message with the server's UUID and IP address + send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to send the election message + send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + send_socket.sendto(msg,(right_neighbour,election_port)) # Send msg to the right neighbor usinf the election_port + send_socket.close() + receive_acknowledgement(msg, right_neighbour, election_port) # Wait for acknowledgment + +def accept(group,erhaltene_uuid,erhaltene_ip): + """ + Function to handle election messages and determine the next steps. + If the message is part of an election (ELECTION), the server compares UUIDs to either forward, update, or declare itself as the leader. + If the message is a new leader announcement (NEW_LEAD), it updates the local leader information and forwards the message. + Leader Election: Based on comparing UUIDs, the server with the highest UUID becomes the leader. + Acknowledgment Mechanism: Ensures that messages are received and processed reliably. + """ + global leader_ip + global is_leader + global last_heartbeat_time + global participating + right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure + if group == ELECTION: # If the received message is part of an election + # Compare the received UUID with the server's own UUID + if erhaltene_uuid > myuuid: # Received UUID is greater, so forward the message without changes + print("{ip_adress}:{} is forwarding without updates.".format(myuuid)) + participating = True + msg = f"{ELECTION}: {erhaltene_uuid}: {erhaltene_ip}".encode() + send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) + send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour + send_socket.close() + receive_acknowledgement(msg, right_neighbour , election_port) + elif erhaltene_uuid < myuuid and participating==False: # Received UUID is smaller, update with the server's own UUID and forward + print("{ip_adress}: {} is updating and forwarding.".format(myuuid)) + participating = True + msg = f"{ELECTION}: {myuuid}: {ip_address}".encode() + send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) + send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour + send_socket.close() + receive_acknowledgement(msg, right_neighbour , election_port) + if erhaltene_uuid == myuuid: # If the server receives its own UUID, it becomes the leader + print("{ip_adress}: {} starts acting as a leader!".format(myuuid)) + participating = False + leader_ip = ip_address # Set leader_ip to own IP + leader = myuuid #Set leader to own uuid + msg = f"{NEW_LEAD}: {myuuid}: {ip_address}".encode() + #if leader_ip != ip_address: # Update leadership status if server was not already the leader bevor the election + is_leader.set_value(True) + send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) + send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour + send_socket.close() + receive_acknowledgement(msg, right_neighbour , election_port) + + if group == NEW_LEAD: # If the received message announces a new leader + if erhaltene_uuid == myuuid: # If the UUID matches, the server has already acknowledged + return + if erhaltene_uuid != myuuid: # Update the leader information and forward the new leader announcement + print("{} acknowledged new leader.".format(myuuid)) + if leader_ip == ip_address: # Check if this server was the Leader bevor the election and set is_leader to False + is_leader.set_value(False) + leader_ip = erhaltene_ip # Update leader_ip + leader = erhaltene_uuid # Update leader + msg = f"{NEW_LEAD}: {erhaltene_uuid}: {erhaltene_ip}".encode() + send_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) + send_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + send_socket.sendto(msg,(right_neighbour,election_port)) # Send message to neighbour + send_socket.close() + receive_acknowledgement(msg, right_neighbour , election_port) + last_heartbeat_time = time.time() + +def zuhören_election(): + """ + Listens for incoming election or leader messages on the configured election socket. + Decodes the message, sends an acknowledgment to the sender, and processes the message via the accept() function. + """ + #sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages + #sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + #sock.bind((ip_address,election_port)) # Bind to the election socket + while True: + sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Create a socket to listen for election messages + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + sock.bind((ip_address,election_port)) + data,addr=sock.recvfrom(4096) # Receive data from other servers + übernahme = data.decode('utf-8') # Decode the received message + grouprec = int(übernahme.split(": ")[0]) # Extract group ID, UUID, and IP address from the message + erhaltene_ip = (übernahme.split(": ")[2]) + erhaltene_uuid2 = uuid.UUID((übernahme.split(": ")[1])) + sock.close() + send_acknowledgement() # Send acknowledgment back to the sender + accept(grouprec,erhaltene_uuid2,erhaltene_ip) # Process the election or new leader message +########################### End - Leader Election ########################### + +########################### Start - Process client messages ########################### +def process_hold_back_queue(): + """ + Processes messages in the hold-back queue in the correct order. + """ + while hold_back_queue: + message = hold_back_queue.popleft() # Remove the oldest message from the queue + message_id, decoded_message = message + if message_id not in processed_message_ids: + # Process or forward the message + print(f"Processing message: {decoded_message}") + broadcast(decoded_message) # Forward the message to all participants + processed_message_ids.add(message_id) # Mark the message as processed + +def broadcast(message): + """ + Sends a message to all participants in the network via broadcast. + """ + server_socket2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket + server_socket2.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode + full_message = f"{message}".encode() # Encode the message + server_socket2.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message + server_socket2.close() + +def listen_client(): + """ + Listens for messages from clients, processes them, and broadcasts them to other participants. + """ + global last_three_messages + server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket + server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode + server_socket.bind(('', client_broadcast_port)) # Bind to the broadcast port + while True: # Wait to receive a message from a client + try: + message, client_address = server_socket.recvfrom(4096) # Receive a message from a client + decoded_message = message.decode() # Decode the message + last_three_messages.append(decoded_message) # Store the message for Leader heartbeat + #print(f"Message stored: {last_three_messages}") # Only for debugging + message_id = decoded_message.split(":")[0] # Extract the unique message ID (UUID) from the decoded message + if message_id in processed_message_ids: # Check if the message has already been processed + continue # Skip if the message was already processed + hold_back_queue.append((message_id, decoded_message)) # Add the message to the hold-back queue + process_hold_back_queue() # Process messages in the hold-back queue + except socket.error as e: # Handle socket errors + print(f"An error occurred while listening: {e}") + break + except KeyboardInterrupt: # Handle server shutdown via keyboard interrupt ????? Funktioniert das?? + print("\nShutting down server...") + break +########################### End - Process client messages ########################### + +########################### Start - Leader Heartbeat ################################# +def forward_received_heartbeat_to_clients(): + """ + Forwards the last 3 received heartbeat messages separately to clients via UDP broadcast. + """ + server_socket3 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket + server_socket3.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode + print("Starting to send the messages from the heartbeat.") + for message in received_heartbeat: + try: + print(f"Forwarding message: {message}") # Print the message being forwarded for debugging + full_message = f"{message}".encode() # Encode the message + server_socket3.sendto(full_message, (broadcast_ip, client_broadcast_port2)) # Send the broadcast message + except Exception as e: + print(f"Error forwarding message: {e}") + server_socket3.close() # Close the socket to free up resources + +def listen_heartbeat(): + """ + Listens for the leader's heartbeat and forwards it to the right neighbor in the ring. + """ + global received_heartbeat + global last_heartbeat_time + #sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats + #sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + #sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening + while True: # Receive data from the socket + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for for receiving heartbeats + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + sock.bind((ip_address, heartbeat_port)) # Bind the socket to the servers IP address and heartbeat port for listening + data, addr = sock.recvfrom(1024) # Receive data from the socket + print("Leader heartbeat received.") + sock.close() + send_acknowledgement() # Send acknowledgment for the received message + received_heartbeat = pickle.loads(data) # Deserialize the received data + #print(received_heartbeat) # Debugging. Print the received messages + last_heartbeat_time = time.time() # Update the last heartbeat timestamp + if leader_ip == ip_address: # If the server is the leader + if send_heartbeat==received_heartbeat: + print(f"My heartbeat has traveled through the ring.") + else: + print(f"Received a foreign heartbeat. I am forwarding the messages.") + forward_received_heartbeat_to_clients() + else: # Forward the heartbeat to the right neighbor + print("Heartbeat is forwarded to the neigbbor.") + right_neighbour = get_neighbour(members_IP, ip_address, 'right') + msg = pickle.dumps(received_heartbeat) + try: + sock_heart = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending Hearbeat + sock_heart.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + sock_heart.sendto(msg, (right_neighbour , heartbeat_port)) # Send heartbeat to the right neighbor + sock_heart.close() # Close the socket to free up resources + receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor + + except Exception as e: # Handle errors during data transmission + print(f"Error sending data lsiten heartbeat: {e}") + + +def leader_send_heartbeat(): + """ + Sends the Heartbeat to the next server in the ring. + """ + global send_heartbeat + global last_heartbeat_time + while True: + send_heartbeat = last_three_messages # Set the send_heartbeat as the last three messages + msg = pickle.dumps(send_heartbeat) # Serialize the messages + right_neighbour = get_neighbour(members_IP, ip_address, 'right') # Determine the right neighbor based on the current ring structure + if len(members_IP) == 1: # If only this server is part of the Ring it does not have to send an Heartbeat + print("I am the only server. There is no other Server to send a heartbeat to.") + last_heartbeat_time = time.time() # Update the last heartbeat timestamp + else: # Send the heartbeat to the right neighbor + try: + sock_heart_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket for sending updates + sock_heart_send.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) + sock_heart_send.sendto(msg, (right_neighbour , heartbeat_port)) # Send the members list to the right neighbor + sock_heart_send.close() + receive_acknowledgement(msg, right_neighbour , heartbeat_port) # Wait for acknowledgment from the right neighbor + except Exception as e: # Handle errors during data transmission + print(f"Error sending data Leader_send_heartbeat: {e}") + sock_heart_send.close() # Close the socket to free up resources + time.sleep(3) # Wait before sending the next heartbeat + +def monitor_heartbeat(): ################ Achtung Testen, ob auch der Leader in ein timeout kommen kann... ggf. vor removal f leader IP einfügen####### + """ + Monitors whether the last heartbeat was received within the timeout period. + """ + global members_IP + global leader_ip + global last_heartbeat_time + while True: + time_since_last_heartbeat = time.time() - last_heartbeat_time # Calculate the time since the last heartbeat + if time_since_last_heartbeat > 11: + print(f"ERROR: No heartbeat received for 11 seconds! Leader is down.") + members_IP.remove(leader_ip) # Remove the Leader server from the member list + if len(members_IP) == 1: # If only one server remains, it becomes the leader + print("I am now the last server in this ring and therefore the leader.") + leader_ip = ip_address # Update the leader IP to the current server + is_leader.set_value(True) + else: + send_update_to_ring() # Update the ring and forward necessary messages + start_election() # Start leader election to determine new Leader + last_heartbeat_time = time.time() # Update the last heartbeat timestamp + time.sleep(1) # Check for heartbeat status every second +########################### End - Leader Heartbeat ############################ + +########################### Start - Client Heartbeat ########################### +def leader_send_Client_heartbeat(): + """ + Broadcasts a heartbeat message to the clients, indicating the server is available. + """ + while True: + msg = ("Server is available.") # Define the heartbeat message + print("I am sending a heartbeat to the client.") # Debugging message indicating that the server is sending a heartbeat + server_client_heartbeat = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Create a UDP socket + server_client_heartbeat.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) # Enable broadcast mode + server_client_heartbeat.sendto(msg.encode('utf-8'), (broadcast_ip, heartbeat_client_broadcast_port)) # # Send the broadcast message to the specified broadcast IP and port + server_client_heartbeat.close()# Close the socket to free up resources + time.sleep(2) # Wait for 15 seconds before sending the next heartbeat +########################### End - Client Heartbeat ########################### + +########################### Start - observation value changes leader ########################### +class VariableWatcher: # A utility class designed to monitor a variable's value and notify registered observers whenever the value changes. + def __init__(self): + self._value = None # Initializes the variable's value as None + self.observers = [] # Creates a list to store observers + def set_value(self, new_value): # Updates the variable and notifies observers. + self._value = new_value # Updates the variable's value + self.notify(new_value) # Notifies all observers about the new value + def add_observer(self, observer): + self.observers.append(observer) # Adds an observer to the list + def notify(self, new_value): + for observer in self.observers: # Iterates through all registered observers + observer(new_value) # Calls each observer with the new value + +def callback(new_value): + """ + This function is triggered when the `is_leader` value changes. + """ + if new_value: + print("I am now taking over leader responsibilities.") + thread7 = threading.Thread(target=listen_client) # Listens to client messages + thread8 = threading.Thread(target=new_server_in_ring) # Listens for election messages + thread9 = threading.Thread(target= leader_send_Client_heartbeat) # Start sending heartbeats to the client + thread10 = threading.Thread(target= leader_send_heartbeat) + thread7.start() + thread8.start() + thread9.start() + if len(received_heartbeat) > 0: # Check if there are values in received_heartbeat + print("I am sending the last 3 messages of the leader heartbeat to the clients.") + forward_received_heartbeat_to_clients() # Forward the heartbeat to the clients. + thread10.start() # Leader starts sending its heartbeat to its right neighbour. + else: + print("I am no longer the leader.") +########################### End - observation value changes leader ########################### + +####################################################################### + +if __name__ == "__main__": + # Create threads for different server operations + thread1 = threading.Thread(target=server_enters) # Handles server entry to the ring + thread2 = threading.Thread(target=lausche_update_Ring) # Listens for ring updates + thread4 = threading.Thread(target=zuhören_election) # Listens for election messages + thread5 = threading.Thread(target=listen_heartbeat) # Listens for leader heartbeat + thread6 = threading.Thread(target=monitor_heartbeat) # Monitord the leader heartbeat + # Start all threads + thread1.start() + thread2.start() + thread4.start() + thread5.start() + thread6.start() + + is_leader = VariableWatcher() # Create an instance of VariableWatcher to observe changes in leader status is_leader.add_observer(callback) # Add the callback function as an observer for changes in `is_leader` \ No newline at end of file diff --git a/neighbour.py b/neighbour.py deleted file mode 100644 index 4b21a5829065c62650aae3c909dce71ccd5ea416..0000000000000000000000000000000000000000 --- a/neighbour.py +++ /dev/null @@ -1,16 +0,0 @@ - -def get_neighbour(member_IP, current_member_ip, direction='left'): - current_member_index = member_IP.index(current_member_ip) if current_member_ip in member_IP else -1 - if current_member_index != -1: - if direction == 'left': - if current_member_index + 1 == len(member_IP): - return member_IP[0] - else: - return member_IP[current_member_index + 1] - else: - if current_member_index - 1 < 0: - return member_IP[len(member_IP) - 1] - else: - return member_IP[current_member_index - 1] - else: - return None \ No newline at end of file