SXXXXXXX_ControlPanel/network.py

159 lines
6.2 KiB
Python

# 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.")
# --- 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."
)