S1005403_RisCC/target_simulator/utils/network.py

168 lines
6.6 KiB
Python

"""Network helpers for UDP socket creation and receive helpers.
This module provides small convenience functions used by the transport
components to create, configure and safely close UDP sockets. The helpers
attempt to tune the socket receive buffer size and handle common UDP
operational errors gracefully.
"""
# 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."
)