2. TCP聊天室

任务目标

实现一个基于TCP的多客户端聊天室:服务器维护多个客户端连接,任一客户端发送的消息由服务器广播给所有其他在线客户端,并处理客户端并发接入与断开。

环境与依赖

Python 3.8+,仅使用标准库 socket、threading,无需第三方包。

核心实现

import socket
import threading

HOST = '127.0.0.1'
PORT = 9000
clients = []
lock = threading.Lock()

def broadcast(message, sender_sock=None):
    with lock:
        for sock in clients[:]:
            if sock is not sender_sock:
                try:
                    sock.sendall(message)
                except:
                    if sock in clients:
                        clients.remove(sock)
                    sock.close()

def handle_client(conn, addr):
    print(f"[连接] {addr} 已加入")
    try:
        while True:
            data = conn.recv(1024)
            if not data:
                break
            broadcast(data, conn)
    except:
        pass
    finally:
        with lock:
            if conn in clients:
                clients.remove(conn)
        conn.close()
        print(f"[断开] {addr} 已离开")

def start_server():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind((HOST, PORT))
    server.listen(5)
    print(f"[启动] 聊天室服务器监听 {HOST}:{PORT}")
    while True:
        conn, addr = server.accept()
        with lock:
            clients.append(conn)
        threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()

if __name__ == '__main__':
    start_server()

运行与验证

启动服务器:

python chat_server.py

启动两个或多个客户端:另开终端,使用 nc 或 Python 交互式连接:

nc 127.0.0.1 9000

每个终端输入一行文字并回车。

通过条件:

  • 任一客户端发送的消息,其他所有客户端都能收到,发送者自己不会收到回显;
  • 服务器终端显示每个客户端的加入与断开日志;
  • 一个客户端断开(Ctrl+C 或关闭终端)不影响其他客户端继续收发消息;
  • 多个客户端同时发送消息时,消息不丢失、不串线。

关键说明

  • 广播时使用 sender_sock 参数排除消息来源,避免回显给发送者自身;
  • 所有对 clients 列表的读写均通过 lock 保护,防止多线程并发修改导致异常;
  • 发送失败(客户端异常断开)时自动从列表移除并关闭连接,保证后续广播不会向失效socket发送数据。