Mehrere Clients und Fehlerbehandlung
In realen Anwendungen muss ein Server typischerweise mehrere Clients gleichzeitig verarbeiten und verschiedene Fehlerbedingungen ordnungsgemäß verwalten. Lassen Sie uns unsere Implementierung unter Berücksichtigung dieser Aspekte verbessern.
Verstehen der gleichzeitigen Client-Verarbeitung
Es gibt verschiedene Möglichkeiten, mehrere Clients gleichzeitig zu verarbeiten:
- Threading (Thread-basiert): Erstellen Sie einen neuen Thread für jede Client-Verbindung
- Prozessbasiert: Erstellen Sie einen neuen Prozess für jeden Client
- Asynchrones I/O: Verwenden Sie nicht-blockierendes I/O mit einer Ereignisschleife
Für dieses Lab implementieren wir einen Threading-basierten Ansatz, der relativ einfach zu verstehen und zu implementieren ist.
Verbessern des Servers für mehrere Clients
Lassen Sie uns unseren Server so modifizieren, dass er mehrere Clients mithilfe von Threads verarbeitet:
- Öffnen Sie die Datei
server.py in der WebIDE und ersetzen Sie den Code durch:
import socket
import threading
def handle_client(client_socket, client_address):
"""Handle communication with a single client"""
try:
print(f"[NEW CONNECTION] {client_address} connected.")
while True:
## Receive client data
try:
data = client_socket.recv(1024)
if not data:
break ## Client disconnected
message = data.decode()
print(f"[{client_address}] {message}")
## Send response
response = f"Message '{message}' received successfully"
client_socket.send(response.encode())
except ConnectionResetError:
print(f"[{client_address}] Connection reset by client")
break
except Exception as e:
print(f"[ERROR] {e}")
finally:
## Clean up when client disconnects
client_socket.close()
print(f"[DISCONNECTED] {client_address} disconnected")
def start_server():
"""Start the server and listen for connections"""
## Server configuration
host = '127.0.0.1'
port = 12345
## Create socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
## Set socket option to reuse address
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
## Bind to host and port
server_socket.bind((host, port))
## Listen for connections
server_socket.listen(5)
print(f"[STARTING] Server is listening on {host}:{port}")
while True:
## Accept client connection
client_socket, client_address = server_socket.accept()
## Create a new thread to handle the client
client_thread = threading.Thread(
target=handle_client,
args=(client_socket, client_address)
)
client_thread.daemon = True ## Thread will close when main program exits
client_thread.start()
## Display active connections
print(f"[ACTIVE CONNECTIONS] {threading.active_count() - 1}")
except KeyboardInterrupt:
print("\n[SHUTTING DOWN] Server is shutting down...")
except Exception as e:
print(f"[ERROR] {e}")
finally:
server_socket.close()
print("[CLOSED] Server socket closed")
if __name__ == "__main__":
start_server()
- Speichern Sie die Datei.
Verbessern des Clients mit Fehlerbehandlung
Lassen Sie uns auch unseren Client mit besserer Fehlerbehandlung verbessern:
- Öffnen Sie die Datei
client.py in der WebIDE und ersetzen Sie den Code durch:
import socket
import sys
import time
def start_client():
"""Start a client that connects to the server"""
## Server information
host = '127.0.0.1'
port = 12345
## Create socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
## Set a timeout for connection attempts (5 seconds)
client_socket.settimeout(5)
try:
## Connect to server
print(f"[CONNECTING] Connecting to server at {host}:{port}...")
client_socket.connect((host, port))
## Reset timeout to none for regular communication
client_socket.settimeout(None)
print("[CONNECTED] Connected to server")
## Communication loop
while True:
## Get user input
message = input("\nEnter message (or 'quit' to exit): ")
if message.lower() == 'quit':
print("[CLOSING] Closing connection by request...")
break
try:
## Send message
client_socket.send(message.encode())
## Wait for response
response = client_socket.recv(1024)
print(f"[RESPONSE] {response.decode()}")
except ConnectionResetError:
print("[ERROR] Connection was reset by the server")
break
except ConnectionAbortedError:
print("[ERROR] Connection was aborted")
break
except Exception as e:
print(f"[ERROR] {e}")
break
except socket.timeout:
print("[TIMEOUT] Connection attempt timed out. Is the server running?")
except ConnectionRefusedError:
print("[REFUSED] Connection refused. Make sure the server is running.")
except KeyboardInterrupt:
print("\n[INTERRUPT] Client shutting down...")
except Exception as e:
print(f"[ERROR] {e}")
finally:
## Close socket
try:
client_socket.close()
print("[DISCONNECTED] Disconnected from server")
except:
pass
if __name__ == "__main__":
start_client()
- Speichern Sie die Datei.
Verstehen des verbesserten Codes
Server-Erweiterungen:
- Wir haben das
threading-Modul hinzugefügt, um mehrere Clients gleichzeitig zu verarbeiten.
- Jede Client-Verbindung wird jetzt in einem separaten Thread verarbeitet.
- Wir haben die Fehlerbehandlung mit spezifischeren Ausnahmen verbessert.
- Wir zeigen die Anzahl der aktiven Client-Verbindungen an.
- Threads werden als "Daemon" gesetzt, was bedeutet, dass sie automatisch geschlossen werden, wenn das Hauptprogramm beendet wird.
Client-Erweiterungen:
- Wir haben ein Verbindungstimeout hinzugefügt, um zu verhindern, dass der Client hängen bleibt, wenn der Server nicht verfügbar ist.
- Wir haben die Fehlerbehandlung mit spezifischen Ausnahmen für verschiedene Netzwerkfehler verbessert.
- Wir haben beschreibendere Statusmeldungen mit klarer Formatierung hinzugefügt.
Testen des Multi-Client-Servers
Lassen Sie uns unsere verbesserten Anwendungen testen:
- Starten Sie den Server:
python3 ~/project/socket_lab/server.py
Sie sollten Folgendes sehen:
[STARTING] Server is listening on 127.0.0.1:12345
- Starten Sie in einem neuen Terminal einen Client:
python3 ~/project/socket_lab/client.py
- Starten Sie einen weiteren Client in einem dritten Terminal:
python3 ~/project/socket_lab/client.py
- Im Server-Terminal sollten Sie beide Verbindungen sehen:
[NEW CONNECTION] ('127.0.0.1', 59124) connected.
[ACTIVE CONNECTIONS] 1
[NEW CONNECTION] ('127.0.0.1', 59126) connected.
[ACTIVE CONNECTIONS] 2
- Senden Sie Nachrichten von beiden Clients und beobachten Sie, wie der Server sie empfängt:
[('127.0.0.1', 59124)] Hello from client 1
[('127.0.0.1', 59126)] Hello from client 2
- Wenn Sie fertig sind, geben Sie in jedem Client 'quit' ein, um die Verbindung zu trennen, oder drücken Sie Strg+C im Server-Terminal, um den Server herunterzufahren.
Umgang mit nicht laufendem Server
Lassen Sie uns auch testen, was passiert, wenn der Server nicht läuft:
-
Stellen Sie sicher, dass der Server gestoppt ist (drücken Sie Strg+C, falls er läuft).
-
Versuchen Sie, einen Client auszuführen:
python3 ~/project/socket_lab/client.py
Sie sollten Folgendes sehen:
[CONNECTING] Connecting to server at 127.0.0.1:12345...
[REFUSED] Connection refused. Make sure the server is running.
[DISCONNECTED] Disconnected from server
Der Client behandelt den Fehler jetzt ordnungsgemäß und informiert den Benutzer, dass der Server möglicherweise nicht läuft.
Mit diesen Erweiterungen haben wir ein robustes Client-Server-System erstellt, das mehrere Clients und verschiedene Fehlerbedingungen verarbeiten kann. Dies ist eine solide Grundlage für die Entwicklung komplexerer Netzwerk-Anwendungen.