Skip to content

Commit d76c181

Browse files
authored
UDP Server
1 parent ed83a9b commit d76c181

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

src/udp_server.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""
2+
udp_server.py
3+
4+
Creates a UDP server that listens for incoming client requests and responds with humidity and temperature data retrieved from the sensor.
5+
The server uses multithreading to handle concurrent client requests. Extensive logging and error handling routines have been added.
6+
"""
7+
8+
import socket
9+
import json
10+
import sys
11+
import threading
12+
import logging
13+
from config import UDP_IP, UDP_PORT, RESPONSE_TIMEOUT, ENABLE_LOGGING, LOG_FILE, DEBUG_MODE
14+
from sensor_reader import SensorReader
15+
16+
# Configure logging for the UDP server
17+
logger = logging.getLogger("UDPServer")
18+
logger.setLevel(logging.DEBUG if DEBUG_MODE else logging.INFO)
19+
console_handler = logging.StreamHandler()
20+
file_handler = logging.FileHandler(LOG_FILE)
21+
formatter = logging.Formatter('%(asctime)s - UDPServer - %(levelname)s - %(message)s')
22+
console_handler.setFormatter(formatter)
23+
file_handler.setFormatter(formatter)
24+
logger.addHandler(console_handler)
25+
logger.addHandler(file_handler)
26+
27+
def handle_client(sock, data, address, sensor_reader):
28+
"""
29+
Handle an individual client's UDP request in a separate thread.
30+
This function reads the sensor data and replies with a JSON payload.
31+
"""
32+
try:
33+
received_message = data.decode().strip()
34+
logger.info("Received request from %s: %s", address, received_message)
35+
sensor_data = sensor_reader.get_data()
36+
response = json.dumps(sensor_data)
37+
sock.sendto(response.encode(), address)
38+
logger.info("Sent response to %s: %s", address, response)
39+
except Exception as e:
40+
logger.exception("Error handling client %s: %s", address, e)
41+
42+
def monitor_performance(sensor_reader):
43+
"""
44+
Periodically logs system performance and sensor data for monitoring.
45+
This function runs in its own thread.
46+
"""
47+
while True:
48+
try:
49+
data = sensor_reader.get_data()
50+
logger.debug("Performance Monitor - Sensor Data: %s", data)
51+
except Exception as e:
52+
logger.error("Error in performance monitoring: %s", e)
53+
# Sleep between performance logs
54+
for _ in range(5):
55+
try:
56+
# This loop also checks for interruption
57+
threading.Event().wait(timeout=1)
58+
except KeyboardInterrupt:
59+
logger.info("Performance monitor interrupted.")
60+
break
61+
62+
def start_udp_server():
63+
"""
64+
Start the UDP server to handle incoming UDP requests.
65+
This function initializes the socket, sensor reader, and monitoring thread.
66+
"""
67+
# Create UDP socket
68+
try:
69+
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
70+
server_socket.bind((UDP_IP, UDP_PORT))
71+
server_socket.settimeout(RESPONSE_TIMEOUT)
72+
logger.info("UDP Server listening on %s:%s", UDP_IP, UDP_PORT)
73+
except Exception as e:
74+
logger.exception("Failed to create or bind UDP socket: %s", e)
75+
sys.exit(1)
76+
77+
# Initialize sensor reader and start its thread
78+
sensor_reader = SensorReader()
79+
sensor_reader.start()
80+
81+
# Start performance monitoring in a separate thread
82+
monitor_thread = threading.Thread(target=monitor_performance, args=(sensor_reader,), daemon=True)
83+
monitor_thread.start()
84+
logger.info("Performance monitoring thread started.")
85+
86+
try:
87+
while True:
88+
try:
89+
data, address = server_socket.recvfrom(1024)
90+
# Log raw request received for debugging purposes
91+
logger.debug("Raw data received: %s from %s", data, address)
92+
# Create a new thread for each incoming client request
93+
client_thread = threading.Thread(
94+
target=handle_client, args=(server_socket, data, address, sensor_reader))
95+
client_thread.start()
96+
except socket.timeout:
97+
logger.debug("Socket timeout waiting for client data. Continuing...")
98+
continue
99+
except Exception as e:
100+
logger.exception("Error receiving data: %s", e)
101+
except KeyboardInterrupt:
102+
logger.info("UDP server shutting down due to keyboard interrupt...")
103+
finally:
104+
sensor_reader.stop()
105+
server_socket.close()
106+
logger.info("UDP server closed. Exiting.")
107+
108+
def main():
109+
"""
110+
Entry point for the UDP server application.
111+
Additional preliminary logging and self-test calls can be added here.
112+
"""
113+
logger.info("Starting UDP server application.")
114+
start_udp_server()
115+
logger.info("UDP server application has stopped.")
116+
117+
# Extra function for future expansion, not currently used.
118+
def advanced_request_handler():
119+
"""
120+
Placeholder for an advanced UDP request handler that might implement
121+
additional protocol features, such as authentication or encryption.
122+
"""
123+
pass
124+
125+
# for further networking features can be integrated.
126+
#
127+
# def udp_multicast_listener():
128+
# """
129+
# Example function to implement an UDP multicast listener in the future.
130+
# """
131+
# pass
132+
#
133+
# End placeholder for extended networking features.
134+
135+
if __name__ == "__main__":
136+
try:
137+
main()
138+
except Exception as e:
139+
logger.exception("Fatal error in UDP server: %s", e)
140+
sys.exit(1)

0 commit comments

Comments
 (0)