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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 17:18:04