You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Socket服务器非阻塞按键检测与客户端列表推送需求

Hey there! Great job getting multi-client socket connections working already—let's get that console input handling integrated smoothly.

The core challenge here is making your server listen for two things at once: new client connections and console input, without blocking either. Let's break down two solid approaches you can use, with code examples tailored to your needs.

Python's built-in selectors module lets you monitor multiple I/O sources (like your server socket and standard input) in a single loop, which is perfect for non-blocking scenarios. Here's how to adapt your server code:

First, set up the selector and track connected clients:

import selectors
import socket
import sys

# Initialize selector
sel = selectors.DefaultSelector()
client_sockets = []
accept_new_connections = True

# Set up server socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("localhost", 12345))  # Use your port
server_socket.listen()
server_socket.setblocking(False)
sel.register(server_socket, selectors.EVENT_READ, data=None)

Next, define functions to handle new connections and console input:

def accept_new_client(sock):
    """Handle incoming client connections"""
    conn, addr = sock.accept()
    print(f"New connection from {addr}")
    conn.setblocking(False)
    client_sockets.append(conn)

def handle_console_input():
    """Process console triggers and send client list"""
    global accept_new_connections
    input_str = sys.stdin.readline().strip()
    
    # Trigger on enter (empty string) or "list" command
    if input_str == "" or input_str.lower() == "list":
        accept_new_connections = False
        print("Stopping new connections — sending client list to all clients...")
        
        # Build client list message
        client_list = "\n".join([
            f"Client {idx+1}: {sock.getpeername()}" 
            for idx, sock in enumerate(client_sockets)
        ])
        message = f"Current connected clients:\n{client_list}".encode("utf-8")
        
        # Send to all clients (handle disconnects gracefully)
        for sock in client_sockets.copy():  # Use copy to avoid modification during loop
            try:
                sock.send(message)
            except Exception as e:
                print(f"Failed to send to {sock.getpeername()}: {str(e)}")
                client_sockets.remove(sock)
                sock.close()
    
    # Optional: Add a "resume" command to start accepting connections again
    elif input_str.lower() == "resume":
        accept_new_connections = True
        print("Resuming new client connections...")

# Register standard input with the selector
sel.register(sys.stdin, selectors.EVENT_READ, data=handle_console_input)

Finally, run the main loop:

try:
    while True:
        # Wait for I/O events (blocks until something happens)
        events = sel.select(timeout=None)
        for key, mask in events:
            if key.data is None:
                # Handle new client only if we're accepting connections
                if accept_new_connections:
                    accept_new_client(key.fileobj)
            else:
                # Run the console input handler
                key.data()
except KeyboardInterrupt:
    print("\nServer shutting down...")
finally:
    # Cleanup all sockets
    sel.close()
    for sock in client_sockets:
        sock.close()
    server_socket.close()

Approach 2: Use Threading (Simpler for Beginners)

If you find the selector approach a bit abstract, you can use a separate thread to listen for console input while your main thread handles client connections. Just remember to use a lock to safely access shared data (like the client_sockets list):

import socket
import sys
import threading

client_sockets = []
accept_new_connections = True
lock = threading.Lock()  # For thread-safe access to shared variables

def console_listener():
    """Thread to listen for console input"""
    global accept_new_connections
    while True:
        input_str = sys.stdin.readline().strip()
        if input_str == "" or input_str.lower() == "list":
            with lock:
                accept_new_connections = False
                # Build and send client list
                client_list = "\n".join([
                    f"Client {idx+1}: {sock.getpeername()}" 
                    for idx, sock in enumerate(client_sockets)
                ])
                message = f"Current connected clients:\n{client_list}".encode("utf-8")
                
                for sock in client_sockets.copy():
                    try:
                        sock.send(message)
                    except Exception as e:
                        print(f"Error sending to client: {str(e)}")
                        client_sockets.remove(sock)
                        sock.close()
        elif input_str.lower() == "resume":
            with lock:
                accept_new_connections = True
                print("Resuming new connections...")

# Start the console listener thread (daemon thread exits when main thread does)
threading.Thread(target=console_listener, daemon=True).start()

# Main server loop
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("localhost", 12345))
server_socket.listen()

try:
    while True:
        with lock:
            if not accept_new_connections:
                continue  # Skip accepting new connections if disabled
        conn, addr = server_socket.accept()
        print(f"New connection from {addr}")
        with lock:
            client_sockets.append(conn)
except KeyboardInterrupt:
    print("\nServer shutting down...")
finally:
    with lock:
        for sock in client_sockets:
            sock.close()
    server_socket.close()

Key Notes to Remember

  • Handle Client Disconnects: Always wrap send calls in try/except blocks—if a client disconnects unexpectedly, you'll get an error, and you should remove it from your client list.
  • Encoding Consistency: Make sure your client code uses the same encoding (like utf-8) when receiving messages.
  • Cross-Platform Compatibility: The selectors approach works on Windows, macOS, and Linux. If you're on Windows and want a simpler key-press detector (instead of waiting for enter), you can use the msvcrt module's kbhit() function, but it's not cross-platform.

Pick the approach that feels most comfortable for your skill level—both will get the job done!

内容的提问来源于stack exchange,提问作者Varshil Anavadia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:25:37