如何取消Python中input()引发的阻塞线程?TCP客户端场景答疑
Hey there! Let's work through your TCP client threading problem with those blocking input() calls — I’ve run into similar scenarios before, so let’s break this down step by step.
The root issue here is that input() is a blocking call: when your client thread is waiting for user input, it can’t simultaneously listen for incoming messages from the server. That means if the server sends a notification to all clients after someone enters 'X', the blocked threads won’t see that message until the user enters something — which defeats the purpose of the real-time notification.
To fix this, we need to split the client’s responsibilities into two separate threads:
- One thread dedicated to handling user input and sending commands to the server
- Another thread dedicated to listening for incoming messages from the server (and acting on them immediately)
By separating these two tasks, neither will block the other. The input thread can wait for user commands without missing server notifications, and the listener thread can react to server messages the moment they arrive.
完整代码示例
客户端代码
This client spins up a daemon thread to listen for server messages, while the main thread handles user input:
import socket import threading def listen_for_server_messages(client_socket): """Thread dedicated to receiving and handling server notifications""" while True: try: # Receive message from server (1024 byte buffer) message = client_socket.recv(1024).decode('utf-8') if not message: print("\nServer disconnected. Exiting...") break # Print server notifications (use \n to avoid overlapping with input prompt) print(f"\n[Server Notification]: {message}") # If you need to trigger specific logic here, add it below # Example: if "continue" in message: do_something() except Exception as e: print(f"\nError receiving message: {str(e)}") break def run_client(): HOST = '127.0.0.1' PORT = 12345 # Create TCP socket and connect to server client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: client_socket.connect((HOST, PORT)) print(f"Connected to server at {HOST}:{PORT}") # Start the message listener thread (daemon=True means it exits when main thread does) listener_thread = threading.Thread(target=listen_for_server_messages, args=(client_socket,), daemon=True) listener_thread.start() # Main loop for user input while True: user_command = input("Enter command (type 'X' to trigger server notification): ") # Send command to server client_socket.send(user_command.encode('utf-8')) if user_command.lower() == 'x': print("Sent 'X' command — waiting for server to notify others...") except ConnectionRefusedError: print("Could not connect to server. Is it running?") except Exception as e: print(f"Client error: {str(e)}") finally: client_socket.close() print("Client disconnected.") if __name__ == "__main__": run_client()
服务器代码
The server needs to track all connected clients, and when it receives an 'X' command, send a notification to every client except the one that sent it:
import socket import threading # List to keep track of all connected client sockets connected_clients = [] def handle_client(client_socket, client_address): print(f"New client connected: {client_address}") connected_clients.append(client_socket) try: while True: # Receive command from client command = client_socket.recv(1024).decode('utf-8') if not command: break print(f"Received from {client_address}: {command}") # If command is 'X', notify all other clients if command == 'X': notification = f"Client {client_address} has triggered the 'X' command — the program will continue!" for client in connected_clients: if client != client_socket: try: client.send(notification.encode('utf-8')) except Exception as e: print(f"Failed to send notification to client: {str(e)}") except Exception as e: print(f"Error handling client {client_address}: {str(e)}") finally: print(f"Client {client_address} disconnected") connected_clients.remove(client_socket) client_socket.close() def run_server(): HOST = '127.0.0.1' PORT = 12345 # Create TCP socket and start listening server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((HOST, PORT)) server_socket.listen(5) print(f"Server running on {HOST}:{PORT} — waiting for clients...") # Accept new clients indefinitely while True: client_socket, client_address = server_socket.accept() # Spin up a new thread to handle each client client_thread = threading.Thread(target=handle_client, args=(client_socket, client_address)) client_thread.start() if __name__ == "__main__": run_server()
- Daemon Threads: The listener thread uses
daemon=Trueso it automatically exits when the main client thread closes — no need to manually manage its lifecycle. - Error Handling: The code includes basic error handling for disconnections and network issues, but you can expand this based on your app’s needs (e.g., reconnection logic).
- Thread Safety: The server’s
connected_clientslist is modified by multiple threads — for production code, you should add a lock (likethreading.Lock()) to prevent race conditions when adding/removing clients. - Input Prompt Cleanup: Using
\nin server notifications ensures they don’t overlap with the user’s input prompt, making the UI cleaner.
内容的提问来源于stack exchange,提问作者lkonrad

