From 2de4bb79bb9fa3bfa7d2aec48da2061c044cc6eb Mon Sep 17 00:00:00 2001
From: Quoc Dao <quoc.dao@student.reutlingen-university.de>
Date: Thu, 25 Jan 2024 20:30:57 +0100
Subject: [PATCH] broadcast -> multicast

---
 client.py | 27 ++++++++++-----------
 server.py | 70 +++++++++++++++++++++++++++++++------------------------
 2 files changed, 54 insertions(+), 43 deletions(-)

diff --git a/client.py b/client.py
index d2952bf..494c508 100644
--- a/client.py
+++ b/client.py
@@ -2,16 +2,17 @@ import socket
 import threading
 from datetime import datetime
 
+MULTICAST_GROUP_IP = '224.1.1.1'
+
 # Broadcast address and port
-BROADCAST_IP = "192.168.0.255"
-BROADCAST_PORT = 5973
+
+CLIENT_MULTICAST_PORT = 5973
 
 # Local host information
 MY_HOST = socket.gethostname()
 MY_IP = socket.gethostbyname(MY_HOST)
 
 
-
 class Client():
     def __init__(self):
         self.currentLeader = ''
@@ -23,23 +24,23 @@ class Client():
         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
-
+    def MulticastSendAndReceive(self):
         # Create a UDP socket
-        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))
+        multicast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 
-        # Send broadcast message
+        # Set the time-to-live for messages to 1 so they do not go past the local network segment
+        multicast_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
+
+        # Send message on multicast address
+        message = 'New client wants to connect: ' + MY_IP
+        multicast_socket.sendto(str.encode(message), (MULTICAST_GROUP_IP, CLIENT_MULTICAST_PORT))
 
         self.printwt("Sent my IP to server group")
 
         while True:     
             try:
                 # receive reply data (server IP) from the other participants
-                reply, addr = broadcast_socket.recvfrom(1024)
+                reply, addr = multicast_socket.recvfrom(1024)
 
                 if reply:
                     # decode received data
@@ -82,6 +83,6 @@ class Client():
 if __name__ == "__main__":
     client = Client()
 
-    thread1 = threading.Thread(target = client.BroadcastSendAndReceive)
+    thread1 = threading.Thread(target = client.MulticastSendAndReceive)
     thread1.start()
     thread1.join()
\ No newline at end of file
diff --git a/server.py b/server.py
index 0894877..e563590 100644
--- a/server.py
+++ b/server.py
@@ -2,14 +2,15 @@ import socket
 import threading
 from datetime import datetime
 import time
+import struct
 
-BROADCAST_IP = "192.168.0.255"
+MULTICAST_GROUP_IP = '224.1.1.1'
 
 # Listening port Server Discovery
-SERVER_BROADCAST_PORT = 5974
+SERVER_MULTICAST_PORT = 5974
 
 # Listening port Client Discovery
-CLIENT_BROADCAST_PORT = 5973
+CLIENT_MULTICAST_PORT = 5973
 
 # Local host information
 MY_HOST = socket.gethostname()
@@ -34,22 +35,26 @@ class Server():
         print("Group view is:", self.serverList)
 
 
-        #This function enables the server to listen to the server broadcast port and reply the ip address
-    def BroadcastListenAndReply(self):
+        #This function enables the server to listen to the server multicast port and reply the ip address
+    def MulticastListenAndReply(self):
 
         # if my IP is not in the server list add it
         if MY_IP not in self.serverList:
             self.serverList.append(MY_IP)
 
         # create socket bind to server address
-        broadcast_listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-        broadcast_listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        broadcast_listen_sock.bind((MY_IP, SERVER_BROADCAST_PORT))
+        multicast_listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        multicast_listen_sock.bind(('', SERVER_MULTICAST_PORT))
+
+        # tell the os to add the socket to the multicast group
+        multicast_group = socket.inet_aton(MULTICAST_GROUP_IP)
+        mreg = struct.pack('4sL', multicast_group, socket.INADDR_ANY)
+        multicast_listen_sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreg)
 
         
         while True:
     
-                data, address = broadcast_listen_sock.recvfrom(1024)
+                data, address = multicast_listen_sock.recvfrom(1024)
 
                 if data:
                     newServer_address = data.decode()
@@ -62,7 +67,7 @@ class Server():
                         self.serverList.append(newServer_address)
 
                     reply_message = MY_IP
-                    broadcast_listen_sock.sendto(str.encode(reply_message), address)
+                    multicast_listen_sock.sendto(str.encode(reply_message), address)
  
                     self.printwt('Replied my IP to new participant')
 
@@ -73,19 +78,20 @@ class Server():
                 self.printwt(f'The current leader IP is: {self.leader_IP}')
             
 
-        #this function enables the server to send a broadcast to the server group and receive the answers of existing members
-    def BroadcastSendAndReceive(self):
+        #this function enables the server to send a multicast to the server group and receive the answers of existing members
+    def MulticastSendAndReceive(self):
     
         # create socket
-        broadcast_group = (BROADCAST_IP, SERVER_BROADCAST_PORT)
-        broadcast_send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-        broadcast_send_sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
-        broadcast_send_sock.settimeout(2)
+        multicast_group = (MULTICAST_GROUP_IP, SERVER_MULTICAST_PORT)
+        multicast_send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        # Set a timeout so the socket does not block indefinitely when trying to receive data.
+        multicast_send_sock.settimeout(2)
 
-        # ...
+         # Set the time-to-live for messages to 1 so they do not go past the local network segment.
+        multicast_send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 1)
 
         message = MY_IP
-        broadcast_send_sock.sendto(message.encode(), broadcast_group)
+        multicast_send_sock.sendto(message.encode(), multicast_group)
         self.printwt("Sent my IP to server group")
 
         # if my IP is not in the server list add it
@@ -106,7 +112,7 @@ class Server():
 
                 try:
                     # receive reply data from the other participants
-                    reply, address = broadcast_send_sock.recvfrom(1024)
+                    reply, address = multicast_send_sock.recvfrom(1024)
 
                     if reply:
                         reply_address = reply.decode()
@@ -126,7 +132,7 @@ class Server():
                     break
 
         if num_responses == 1:     
-            broadcast_send_sock.close()
+            multicast_send_sock.close()
             self.isLeader = True
             self.leader_IP = MY_IP # Hier wird die IP-Adresse des Leaders zugewiesen
             self.printwt(f'I am the only server in the system, so the leader IP is: {self.leader_IP}')
@@ -136,19 +142,23 @@ class Server():
         self.print_group_view()
         
 
-    # Listen to client broadcast (request) and reply with Server IP
+    # Listen to client multicast (request) and reply with Server IP
     def ListenForClientAndReply(self):
         # Create a UDP socket
         listen_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-        # Set the socket to broadcast and enable reusing addresses
-        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+        # Enable reusing addresses
         listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
         # Bind socket to address and port
-        listen_socket.bind((MY_IP, CLIENT_BROADCAST_PORT))
+        listen_socket.bind((MY_IP, CLIENT_MULTICAST_PORT))
+
+        # tell the os to add the socket to the multicast group
+        multicast_group = socket.inet_aton(MULTICAST_GROUP_IP)
+        mreg = struct.pack('4sL', multicast_group, socket.INADDR_ANY)
+        listen_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreg)
 
-        # print("Listening to broadcast messages")
+        # print("Listening to multicast messages")
 
-        # Receiving broadcast massage
+        # Receiving multicast massage
         while True:
             data, addr = listen_socket.recvfrom(1024)
 
@@ -169,13 +179,13 @@ class Server():
                 data = client_socket.recv(1024)
                 if not data:
                     break
-                self.broadcast(data, client_socket)
+                self.multicast(data, client_socket)
 
             except:
                 self.clients.remove(client_socket)
                 break
 
-    def broadcast(self, message, sender_socket):
+    def multicast(self, message, sender_socket):
         for client in self.clients:
             try:
                 if client != sender_socket:
@@ -188,10 +198,10 @@ class Server():
 if __name__== '__main__':
     server = Server()
 
-    thread2 = threading.Thread(target = server.BroadcastListenAndReply)
+    thread2 = threading.Thread(target = server.MulticastListenAndReply)
     thread2.start()
 
-    thread3 = threading.Thread(target = server.BroadcastSendAndReceive)
+    thread3 = threading.Thread(target = server.MulticastSendAndReceive)
     thread3.start()
 
     thread1 = threading.Thread(target = server.ListenForClientAndReply)
-- 
GitLab