From 4e3b5b9c8c83ae51887a7578bfb481ab7facc4ef Mon Sep 17 00:00:00 2001
From: Quoc Dao <quoc.dao@student.reutlingen-university.de>
Date: Tue, 23 Jan 2024 00:29:04 +0100
Subject: [PATCH] mit server-server-dd

---
 client.py | 100 ++++++++++++++++++++--------------------------
 server.py | 117 +++++++++++++++++++++++++++++++++---------------------
 2 files changed, 114 insertions(+), 103 deletions(-)

diff --git a/client.py b/client.py
index c2f81ac..6b342a2 100644
--- a/client.py
+++ b/client.py
@@ -2,9 +2,11 @@ import socket
 import threading
 from datetime import datetime
 
-# Broadcast address and port
-BROADCAST_IP = "192.168.0.255"
-BROADCAST_PORT = 5973
+
+# Broadcast address and port for client broadcasts
+BROADCAST_IP_CLIENT = "192.168.0.255"
+BROADCAST_PORT_CLIENT = 5973
+
 
 # Local host information
 MY_HOST = socket.gethostname()
@@ -17,73 +19,62 @@ class Client():
         self.currentLeader = ''
         self.server_socket = None
 
+
     # print the current date and time
     def printwt(self, msg):
         current_date_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
         print(f'[{current_date_time}] {msg}')    
 
+
     # dynamic discoverey: client sends request to server group and gets the IP of server as reply
     def BroadcastSendAndReceive(self):
-
         message = 'New client wants to connect: ' + MY_IP
 
         # Create a UDP socket
-        broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        client_broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
         # Send message on broadcast address
-        broadcast_socket.sendto(str.encode(message), (BROADCAST_IP, BROADCAST_PORT))
+        client_broadcast_socket.sendto(str.encode(message), (BROADCAST_IP_CLIENT, BROADCAST_PORT_CLIENT))
 
         # Send broadcast message
 
         self.printwt("Sent my IP to server group")
 
-        while True:     
-            try:
-                # receive reply data (server IP) from the other participants
-                reply, addr = broadcast_socket.recvfrom(1024)
-
-                if reply:
-                    # decode received data
-                    reply_addr = reply.decode()
-                    self.currentLeader = reply_addr
-                    self.printwt(f'Got Leader address: {self.currentLeader}')
-
-                    # Connect to the server using TCP
-                    self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                    self.server_socket.connect((self.currentLeader, 5555))
-                    print("You have entered the chat room")
-
-                     # Starte einen Thread, um Nachrichten zu empfangen
-                    receive_thread = threading.Thread(target=self.receive_messages)
-                    receive_thread.start()
-
-                    # Haupt-Thread zum Senden von Nachrichten
-                    while True:
-                        message = input()
-                        timestamped_message = f'[{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] {message}'
-                        self.server_socket.send(timestamped_message.encode('utf-8'))
-                        
-            except socket.timeout:
-                pass
-
-    def discover_group_members(self):
-        # Send a broadcast message to discover group members
-        group_discovery_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-        group_discovery_socket.settimeout(3)  # Set timeout to 3 seconds
-        group_discovery_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
-        group_discovery_socket.sendto(str.encode("Any group members here?"), ('<broadcast>', BROADCAST_PORT))
-
+       # while True:     
+        # Receive server leader's IP
         try:
-            while True:
-                data, addr = group_discovery_socket.recvfrom(1024)
-                if data:
-                    group_member_IP = data.decode()
-                    if group_member_IP != MY_IP and group_member_IP not in self.group_members:
-                        self.group_members[group_member_IP] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
-                        self.printwt(f'New group member found: {group_member_IP}')
-                        # Respond to the new member with own IP
-                        group_discovery_socket.sendto(str.encode(MY_IP), (group_member_IP, BROADCAST_PORT))
+            data, addr = client_broadcast_socket.recvfrom(1024)
+            if data:
+                self.current_leader = data.decode()
+                self.printwt(f'Got Server Leader address: {self.current_leader}')
+                    
+                    
+            # receive reply data (server IP) from the other participants
+           # reply, addr = client_broadcast_socket.recvfrom(1024)
+
+            # if reply:
+            #    # decode received data
+                #   reply_addr = reply.decode()
+                #  self.currentLeader = reply_addr
+                # self.printwt(f'Got Leader address: {self.currentLeader}')
+
+                # Connect to the server using TCP
+                self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                self.server_socket.connect((self.currentLeader, 5555))
+                print("You have entered the chat room")
+
+                    # Starte einen Thread, um Nachrichten zu empfangen
+                receive_thread = threading.Thread(target=self.receive_messages)
+                receive_thread.start()
+
+                # Main thread for sending messages
+                while True:
+                    message = input()
+                    timestamped_message = f'[{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] {message}'
+                    self.server_socket.send(timestamped_message.encode('utf-8'))
+                        
         except socket.timeout:
-            group_discovery_socket.close()
+            pass
+
 
     def receive_messages(self):
         while True:
@@ -102,11 +93,6 @@ class Client():
 if __name__ == "__main__":
     client = Client()
 
-    # Start a thread to discover group members
-    discovery_thread = threading.Thread(target=client.discover_group_members)
-    discovery_thread.start()
-
-    # Start the thread to send and receive messages
     thread1 = threading.Thread(target = client.BroadcastSendAndReceive)
     thread1.start()
     thread1.join()
\ No newline at end of file
diff --git a/server.py b/server.py
index 5b0a521..5551540 100644
--- a/server.py
+++ b/server.py
@@ -3,8 +3,10 @@ import threading
 from datetime import datetime
 import time
 
-# Listening port
-BROADCAST_PORT = 5973
+# Listening port for client broadcasts
+BROADCAST_PORT_CLIENT = 5973
+# Listening port for server broadcasts
+BROADCAST_PORT_SERVER = 5974
 
 # Local host information
 MY_HOST = socket.gethostname()
@@ -15,6 +17,7 @@ class Server():
         self.group_members = {}  # Dictionary to store group members with their IPs as keys
         self.leader_IP = '' # fix the leader IP
         self.clients = []
+        self.is_leader = False   # Flag to indicate if this server is the leader
 
 
     def printwt(self, msg):
@@ -22,38 +25,74 @@ class Server():
         print(f'[{current_date_time}] {msg}')
 
 
+    def announce_leader(self):
+        # Announce leader to clients
+        while True:
+            if self.is_leader:
+                self.broadcast_leader()
+            time.sleep(5)
+
+
+    def broadcast_leader(self):
+        # Broadcast the leader's IP to clients
+        for client_socket in self.clients:
+            try:
+                client_socket.send(self.leader_IP.encode())
+            except:
+                self.clients.remove(client_socket)
+
+
+    def ListenForServerBroadcastAndReply(self):
+        # Listen to server broadcasts and reply with own IP
+        server_broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        server_broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        server_broadcast_socket.bind(('', BROADCAST_PORT_SERVER))
+
+        while True:
+            data, addr = server_broadcast_socket.recvfrom(1024)
+            if data:
+                # Decode received data (new server's IP)
+                new_server_ip = data.decode()
+                self.group_members[new_server_ip] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+
+                # Respond to the new member with own IP
+                server_broadcast_socket.sendto(MY_IP.encode(), addr)
+                self.printwt(f'New group member found: {new_server_ip}')
+                self.print_group_members()
+
+
     def print_group_members(self):
         self.printwt("Current group members:")
         for member, timestamp in self.group_members.items():
             self.printwt(f'{member} (joined at {timestamp})')
 
 
+    def become_leader(self):
+        # Announce to the group that this server is the leader
+        self.is_leader = True
+        self.leader_IP = MY_IP
+        self.broadcast_leader()
+        
+
     # Listen to client broadcast (request) and reply with Server IP
-    def ListenForClientAndReply(self):
+    def listen_for_client_broadcasts(self):
         # Create a UDP socket
-        listen_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        client_broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
         # Set the socket to broadcast and enable reusing addresses
-        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
-        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        client_broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         # Bind socket to address and port
-        listen_socket.bind((MY_IP, BROADCAST_PORT))
+        client_broadcast_socket.bind(('', BROADCAST_PORT_CLIENT))
 
         print("Listening to broadcast messages")
 
         # Receiving broadcast massage
         while True:
-            data, addr = listen_socket.recvfrom(1024)
-
-            if data:
-                self.printwt(data.decode())
+            data, addr = client_broadcast_socket.recvfrom(1024)
 
-        # if Iam the leader, answer the client including my IP
-                if MY_IP == self.leader_IP:
-                    reply_message = MY_IP
-                    listen_socket.sendto(str.encode(reply_message), addr)
+            if data and self.is_leader:
+                    client_broadcast_socket.sendto(self.leader_IP.encode(), addr)
                     self.printwt('Replied my IP to new client')
 
-
     def handle_client(self, client_socket, client_address):
         self.clients.append(client_socket)
 
@@ -68,7 +107,6 @@ class Server():
                 self.clients.remove(client_socket)
                 break
 
-
     def broadcast(self, message, sender_socket):
         for client in self.clients:
             try:
@@ -78,49 +116,36 @@ class Server():
                 self.clients.remove(client)
 
 
-    def announce_leader(self):
-        # Send a broadcast message to determine the leader
-        leader_broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-        leader_broadcast_socket.settimeout(3)  # Set timeout to 3 seconds
-        leader_broadcast_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
-        leader_broadcast_socket.sendto(str.encode("Who is the leader?"), ('<broadcast>', BROADCAST_PORT))
-
-        try:
-            data, addr = leader_broadcast_socket.recvfrom(1024)
-            if data:
-                self.leader_IP = addr[0]
-                self.group_members[self.leader_IP] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
-                self.printwt(f'Leader elected: {self.leader_IP}')
-       
-        except socket.timeout:
-            # If no response received, assume this server is the leader
-            self.leader_IP = MY_IP
-            self.group_members[self.leader_IP] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
-            self.printwt(f'I am the first server and leader: {self.leader_IP}')
-
-        leader_broadcast_socket.close()
-
 # starting all simultaneously working procedures
 if __name__== '__main__':
     server = Server()
 
-    # Start a thread to announce the leader
+
+    # Start a thread to announce the leader to clients
     leader_thread = threading.Thread(target=server.announce_leader)
     leader_thread.start()
 
-     # Start the thread to listen for client broadcasts
-    thread1 = threading.Thread(target = server.ListenForClientAndReply)
-    thread1.start()
 
-    # Start a thread to periodically print the group members
-    print_group_thread = threading.Thread(target=server.print_group_members)
-    print_group_thread.start()
+    # Start a thread to listen for server broadcasts
+    server_broadcast_thread = threading.Thread(target=server.ListenForServerBroadcastAndReply)
+    server_broadcast_thread.start()
+
+
+    client_broadcast_thread = threading.Thread(target = server.listen_for_client_broadcasts)
+    client_broadcast_thread.start()
+
 
     # Socket erstellen und binden
     server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
     server_socket.bind((MY_IP, 5555))
     server_socket.listen(5)
 
+
+     # Check if this server is the first in the group and become the leader
+    time.sleep(3)  # Wait for other servers to potentially announce themselves
+    if not server.group_members:
+        server.become_leader()
+
     while True:
         client_socket, client_address = server_socket.accept()
 
-- 
GitLab