# network.py """ THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. Handles basic UDP socket operations like creation, configuration (buffer size), and closing for the application. Uses standardized logging prefixes. """ # Standard library imports import socket import logging import sys # Keep sys import if needed for platform-specific checks (not currently used) # No local application imports needed in this module typically def create_udp_socket(local_ip, local_port): """ Creates a UDP socket, attempts to increase its receive buffer size, and binds it to the specified local IP address and port. Args: local_ip (str): The local IP address to bind to. local_port (int): The local port to bind to. Returns: socket.socket: The created and bound UDP socket, or None on error. """ log_prefix = "[Network]" # Standard prefix for this module logging.debug( f"{log_prefix} Attempting to create UDP socket for {local_ip}:{local_port}" ) try: # Create the UDP socket (IPv4) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) logging.debug(f"{log_prefix} Socket object created.") # Set a short timeout to prevent the receive loop from blocking indefinitely try: sock.settimeout(0.1) except Exception: # Some test stubs may not implement settimeout; tolerate and continue logging.debug(f"{log_prefix} Socket object has no settimeout; continuing without timeout.") # --- Receive Buffer Size Adjustment --- try: # Get the default buffer size (INFO level is okay for this setup detail) default_rcvbuf = sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) logging.info( f"{log_prefix} Default socket receive buffer size: {default_rcvbuf} bytes" ) # Set a larger buffer size (adjust value as needed) desired_rcvbuf = 8 * 1024 * 1024 # Request 8MB logging.debug( f"{log_prefix} Requesting receive buffer size: {desired_rcvbuf} bytes" ) sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, desired_rcvbuf) # Verify the actual size set by the OS (INFO level okay) actual_rcvbuf = sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF) logging.info( f"{log_prefix} Actual receive buffer size set: {actual_rcvbuf} bytes" ) if actual_rcvbuf < desired_rcvbuf: # WARNING if OS limited the size logging.warning( f"{log_prefix} OS capped the receive buffer size lower than requested." ) except OSError as buf_e: # ERROR if setting buffer fails (might indicate permissions or OS limits issue) logging.error( f"{log_prefix} Failed to set receive buffer size: {buf_e}. Check OS limits (e.g., sysctl net.core.rmem_max)." ) except Exception as buf_e: # Keep EXCEPTION for unexpected errors during buffer setting logging.exception( f"{log_prefix} Unexpected error setting receive buffer: {buf_e}" ) # --- Bind the Socket --- logging.debug(f"{log_prefix} Binding socket to ('{local_ip}', {local_port})...") sock.bind((local_ip, local_port)) # INFO for successful bind confirmation logging.info( f"{log_prefix} UDP socket created and bound successfully to {local_ip}:{local_port}" ) return sock except socket.error as e: # ERROR for critical socket creation/binding failure logging.error( f"{log_prefix} Error creating/binding UDP socket for {local_ip}:{local_port}: {e}" ) return None except Exception as e: # Keep EXCEPTION for any other unexpected errors logging.exception( f"{log_prefix} Unexpected error during UDP socket creation for {local_ip}:{local_port}: {e}" ) return None def receive_udp_packet(sock, buffer_size=65535): """ Receives a single UDP packet from the specified socket. Note: Currently unused by the UdpReceiver class which uses sock.recvfrom directly. Args: sock (socket.socket): The UDP socket to receive from. buffer_size (int): The maximum buffer size for receiving data. Returns: tuple: A tuple containing (data, sender_address), or (None, None) on error. """ log_prefix = "[Network]" # Standard prefix # DEBUG for function call (if used) logging.debug( f"{log_prefix} Attempting to receive UDP packet (buffer size: {buffer_size})." ) try: data, addr = sock.recvfrom(buffer_size) logging.debug(f"{log_prefix} Received {len(data)} bytes from {addr}.") return data, addr except socket.timeout: # DEBUG for expected timeout (if timeout is set on socket) logging.debug(f"{log_prefix} Socket recvfrom timeout.") return None, None except socket.error as e: # ERROR for socket errors during receive logging.error(f"{log_prefix} Error receiving UDP packet: {e}") return None, None except Exception as e: # Keep EXCEPTION for unexpected errors logging.exception(f"{log_prefix} Unexpected error receiving UDP packet: {e}") return None, None def close_udp_socket(sock): """ Closes the specified UDP socket gracefully. Args: sock (socket.socket): The UDP socket object to close. """ log_prefix = "[Network]" # Standard prefix # Check if socket is valid before attempting close if sock and hasattr(sock, "fileno") and sock.fileno() != -1: try: # INFO for significant action: closing the main socket logging.info(f"{log_prefix} Closing UDP socket (fd={sock.fileno()})...") sock.close() logging.info(f"{log_prefix} UDP socket closed successfully.") except socket.error as e: # ERROR if closing fails logging.error(f"{log_prefix} Error closing UDP socket: {e}") except Exception as e: # Keep EXCEPTION for unexpected errors logging.exception(f"{log_prefix} Unexpected error closing UDP socket: {e}") else: # DEBUG if socket is already closed or invalid logging.debug( f"{log_prefix} Socket close requested, but socket was already closed or invalid." )