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

Python聊天服务器消息无法转发至其他客户端问题求助

Python Socket 聊天服务器无法转发消息的问题修复

核心问题分析

  1. 客户端接收线程仅执行一次:原客户端recive()函数只调用一次recv()就结束线程,后续消息无法接收。
  2. 服务器重复调用listen():listen()只需在绑定端口后调用一次,放在accept()循环内会导致不必要的重复初始化监听队列。
  3. 客户端列表的线程安全问题:多线程环境下直接遍历修改clients列表,可能引发迭代异常或数据不一致。
  4. 未处理客户端断开清理:客户端断开后未从clients列表移除,后续转发会尝试向已关闭的socket发送数据,抛出异常。

修改后的服务器代码

import socket
import threading

# 全局线程锁,保护clients和names列表的操作
clients_lock = threading.Lock()
clients = []
names = []

def handle_client(client_socket):
    global clients, names
    # 获取当前客户端的用户名
    try:
        username = client_socket.recv(1024).decode('utf-8').strip()
    except:
        client_socket.close()
        return
    
    with clients_lock:
        clients.append(client_socket)
        names.append(username)
    print(f"Accepted connection from {client_socket.getpeername()} with name {username}")

    try:
        while True:
            data = client_socket.recv(1024)
            if not data:
                break
            message = data.decode('utf-8').strip()
            full_message = f"{username}: {message}"
            print(f"Received message: {full_message}")

            # 转发消息给所有其他客户端
            with clients_lock:
                for sock in clients:
                    if sock != client_socket:  # 跳过发送消息的客户端自己
                        try:
                            sock.sendall(full_message.encode('utf-8'))
                        except:
                            # 发送失败,说明客户端已断开,从列表移除
                            sock.close()
                            idx = clients.index(sock)
                            clients.pop(idx)
                            names.pop(idx)
    except Exception as e:
        print(f"Client error: {e}")
    finally:
        # 清理断开的客户端
        with clients_lock:
            if client_socket in clients:
                idx = clients.index(client_socket)
                clients.pop(idx)
                names.pop(idx)
        client_socket.close()
        print(f"Client {username} disconnected")

def main():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    host = '127.0.0.1'
    port = 8080
    server_socket.bind((host, port))
    server_socket.listen(5)  # 调用一次即可,设置监听队列大小为5
    print(f"Server listening on {host}:{port}")

    while True:
        client_socket, client_address = server_socket.accept()
        client_handler = threading.Thread(target=handle_client, args=(client_socket,))
        client_handler.start()

if __name__ == "__main__":
    main()

修改后的客户端代码

import socket
import threading

def receive_messages(client_socket):
    # 持续接收服务器转发的消息
    while True:
        try:
            data = client_socket.recv(1024)
            if not data:
                print("Disconnected from server")
                break
            msg = data.decode('utf-8')
            print(f"\n{msg}")
            print("Enter your message: ", end="", flush=True)  # 恢复输入提示
        except:
            print("Error receiving message")
            client_socket.close()
            break

def main():
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    host = '127.0.0.1'
    port = 8080
    username = input('Username: ').strip()
    if not username:
        username = "Anonymous"
    
    try:
        client_socket.connect((host, port))
        client_socket.sendall(username.encode('utf-8'))
        
        # 启动接收线程
        recive_handler = threading.Thread(target=receive_messages, args=(client_socket,))
        recive_handler.daemon = True  # 设置为守护线程,主程序退出时自动结束
        recive_handler.start()

        while True:
            message = input("Enter your message: ")
            if message.lower() == 'exit':
                break
            client_socket.sendall(message.encode('utf-8'))
    except Exception as e:
        print(f"Connection error: {e}")
    finally:
        client_socket.close()
        print("Connection closed")

if __name__ == "__main__":
    main()

关键修改说明

  1. 客户端接收逻辑:将单次接收改为循环接收的receive_messages(),设置守护线程确保持续监听服务器消息。
  2. 服务器监听逻辑:将server_socket.listen()移到accept()循环外,仅调用一次。
  3. 线程安全保护:添加clients_lock,在读写clients列表时加锁,避免多线程冲突。
  4. 客户端断开清理:在客户端断开或发送失败时,从列表中移除对应项,避免无效socket操作。
  5. 消息格式优化:转发消息时带上用户名,区分不同用户的消息。
  6. 异常处理增强:添加多处异常捕获,提升程序稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 21:50:19