Python Socket TCP多客户端服务端仅向最后连接客户端发数据问题求助
问题根因排查
你遇到的问题核心是TCP套接字使用逻辑完全错误,具体问题点如下:
sendto是UDP套接字专用的API,对已经建立连接的TCP套接字而言,sendto传入的地址参数会被直接忽略,数据只会发给当前conn对象对应的唯一客户端,这就是你只能给最后接入的客户端发数据的核心原因- 客户端列表存储的是客户端IP+端口,而非每个客户端对应的独立TCP连接对象
conn,根本没有渠道向其他客户端发送数据 - 命令输入逻辑
input写在了每个客户端的处理线程中,会导致每个客户端线程都阻塞等待输入,逻辑完全混乱,命令输入应该放在主线程处理 - 多线程操作全局
clients列表没有加锁,存在线程安全风险,会出现列表读写冲突
修复后的服务端代码
import socket import sys import threading HEADER = 64 PORT = 5050 SERVER = "192.168.0.117" ADDR = (SERVER, PORT) FORMAT = 'utf-8' DISCONNECT_MESSAGE = "!DISCONNECT" server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(ADDR) server.settimeout(2) # 现在存储的是 (conn, addr) 元组 clients = [] # 加线程锁 clients_lock = threading.Lock() def handle_client(conn, addr): print(f"[NEW CONNECTION] {addr} connected.") with clients_lock: clients.append((conn, addr)) print(f"[当前在线客户端] {[a for c,a in clients]}") # 这个线程只负责处理客户端主动发来的消息(如命令执行结果) try: while True: msg = conn.recv(2048).decode(FORMAT) if msg == DISCONNECT_MESSAGE: break if msg: print(f"[{addr}] 返回结果:{msg}") except: pass finally: # 客户端断开后从列表移除 with clients_lock: if (conn, addr) in clients: clients.remove((conn, addr)) conn.close() print(f"[客户端断开] {addr} 已下线,当前在线 {len(clients)} 个客户端") def broadcast_command(command): # 广播命令给所有在线客户端 with clients_lock: for conn, addr in clients: try: # TCP直接用send发就行,不需要sendto conn.send(command.encode(FORMAT)) print(f"[已发送] 命令 [{command}] 到 {addr}") except: pass def start(): server.listen() print(f"[LISTENING] Server is listening on {SERVER}") # 启动单独线程处理命令输入,避免阻塞accept逻辑 def input_thread(): while True: command = input("[SERVER] => ") if command == DISCONNECT_MESSAGE: broadcast_command(command) server.close() sys.exit(0) broadcast_command(command) threading.Thread(target=input_thread, daemon=True).start() while True: try: conn, addr = server.accept() thread = threading.Thread(target=handle_client, args=(conn, addr)) thread.start() except socket.timeout: continue except socket.error as e: print(e) sys.exit(1) if __name__ == '__main__': start()
客户端优化建议
你当前的客户端仅实现了命令执行逻辑,缺少命令执行结果回传的功能,可自行在command_bot中捕获os.system的输出,调用客户端的send方法回传给服务端即可。如果后续要使用你预先定义的HEADER长度校验逻辑,需要保证服务端和客户端收发逻辑对齐,即先发送内容长度再发送内容本身,避免粘包问题。
内容的提问来源于stack exchange,提问作者DaveX PL
相关产品推荐
相关产品推荐

