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

Python Socket程序多客户端与服务器连接维持问题求解

解决Python Socket单客户端阻塞问题

踩过这个坑的人来给你解答!你的问题本质是基础Socket服务器默认是单线程阻塞模式——服务器主线程在和第一个客户端建立连接后,就会一直卡在和这个客户端的交互逻辑里,根本腾不出手去接受新的连接。等到第二个客户端尝试连接时,要么服务器没响应导致第一个客户端超时断开,要么服务器的连接被新请求覆盖,自然就出现了“新客户端连接就踢掉旧的”情况。

核心解决方案:多线程处理客户端连接

给每个新连接的客户端单独分配一个线程,让服务器主线程专职负责监听新请求,每个子线程专门处理对应客户端的通信。这样服务器就能同时应对多个客户端的连接了。

修改后的服务器代码(server.py)

import socket
import threading

# 存储客户端名称的列表,加锁保证线程安全
client_names = []
name_lock = threading.Lock()

def handle_client(client_socket, addr):
    """单独处理一个客户端的交互逻辑"""
    client_name = ""
    try:
        # 接收客户端发送的名称
        client_name = client_socket.recv(1024).decode('utf-8')
        if not client_name:
            print(f"来自{addr}的客户端未发送名称,已断开连接")
            return

        # 线程安全地添加客户端名称到列表
        with name_lock:
            client_names.append(client_name)
        print(f"✅ 新客户端连接:{addr},名称:{client_name}")
        print(f"当前在线客户端:{client_names}")

        # 给客户端发送欢迎响应
        welcome_msg = f"欢迎你,{client_name}!当前共有{len(client_names)}位小伙伴在线~"
        client_socket.send(welcome_msg.encode('utf-8'))

        # 如果需要持续和客户端通信,可以在这里加循环
        # 示例:接收客户端消息并回复
        # while True:
        #     msg = client_socket.recv(1024).decode('utf-8')
        #     if not msg or msg.lower() == "quit":
        #         break
        #     reply = f"收到{client_name}的消息:{msg}"
        #     client_socket.send(reply.encode('utf-8'))

    except Exception as e:
        print(f"❌ 处理{addr}客户端时出错:{str(e)}")
    finally:
        # 客户端断开后,线程安全地移除名称
        with name_lock:
            if client_name in client_names:
                client_names.remove(client_name)
        print(f"❌ 客户端{addr}已断开,当前在线客户端:{client_names}")
        client_socket.close()

def main():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 设置地址复用,避免服务器重启时出现"地址已被占用"的错误
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(('localhost', 12345))
    # 监听队列长度设为5,表示最多同时等待5个未处理的连接请求
    server_socket.listen(5)
    print("🚀 服务器启动成功,等待客户端连接...")

    while True:
        # 主线程一直监听新的连接请求
        client_socket, client_addr = server_socket.accept()
        # 为新客户端创建一个线程处理交互
        client_thread = threading.Thread(target=handle_client, args=(client_socket, client_addr))
        # 设置为守护线程,这样服务器主线程退出时,所有子线程会自动结束
        client_thread.daemon = True
        # 启动线程
        client_thread.start()

if __name__ == "__main__":
    main()

客户端代码(client.py)

基本不需要改动,保持原来的逻辑即可,示例如下:

import socket

def main():
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        client_socket.connect(('localhost', 12345))
        name = input("请输入你的昵称:")
        client_socket.send(name.encode('utf-8'))
        
        # 接收服务器的欢迎消息
        response = client_socket.recv(1024).decode('utf-8')
        print("服务器回复:", response)

        # 如果需要持续聊天,取消下面的注释
        # while True:
        #     msg = input("请输入要发送的消息(输入quit退出):")
        #     client_socket.send(msg.encode('utf-8'))
        #     if msg.lower() == "quit":
        #         break
        #     reply = client_socket.recv(1024).decode('utf-8')
        #     print("服务器回复:", reply)
    except Exception as e:
        print(f"连接出错:{str(e)}")
    finally:
        client_socket.close()

if __name__ == "__main__":
    main()

关键改动说明

  1. 拆分逻辑:把和单个客户端的交互逻辑封装到handle_client函数里,每个客户端对应一个线程执行这个函数。
  2. 线程安全:用threading.Lock()保证多个线程修改共享列表client_names时不会出现数据混乱。
  3. 主线程专注监听:主线程只负责accept()接收新连接,然后启动子线程处理,不会被单个客户端的交互阻塞。
  4. 地址复用:添加SO_REUSEADDR选项,解决服务器重启时的地址占用问题。

其他可选方案

如果你的场景需要更高的并发量,也可以尝试:

  • 多进程:用multiprocessing模块代替threading,适合CPU密集型场景(不过Socket通信是IO密集型,线程更合适)。
  • 异步IO:用asyncio模块写异步Socket服务器,代码风格不同,但并发效率更高,适合大量客户端连接的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:36:58