166 lines
6.6 KiB
Python
166 lines
6.6 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.")
|
|
|
|
# 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."
|
|
)
|