diff --git a/2025-01-10_Client_V6.py b/2025-01-10_Client_V6.py new file mode 100644 index 0000000000000000000000000000000000000000..fd451b48b1dd08d90bf7e416f152e9f8475e206a --- /dev/null +++ b/2025-01-10_Client_V6.py @@ -0,0 +1,136 @@ +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 > 7: # 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()