2. TCP聊天室

任务目标

实现一个基于TCP协议的多客户端聊天室服务端与客户端程序。服务端能够同时接受多个客户端连接,并将任一客户端发送的消息广播给所有在线客户端,从而掌握多线程处理并发连接的核心方法。

环境与依赖

  • Python 3.6+(标准库,无需第三方依赖)
  • 操作系统:Windows / Linux / macOS 均可

核心实现

服务端采用socketserver.ThreadingTCPServer,每个客户端连接由独立线程处理,实现并发广播。客户端使用标准socket库,通过两个线程分别处理消息发送与接收。

# server.py - TCP聊天室服务端
import socketserver

class ChatHandler(socketserver.StreamRequestHandler):
    clients = set()  # 所有在线客户端的socket集合

    def setup(self):
        super().setup()
        self.__class__.clients.add(self.request)
        self.broadcast(f"[系统] 新用户加入,当前在线 {len(self.clients)} 人")

    def handle(self):
        while True:
            try:
                data = self.rfile.readline().strip()
                if not data:
                    break
                msg = data.decode('utf-8')
                self.broadcast(f"[用户] {msg}")
            except (ConnectionResetError, BrokenPipeError):
                break

    def finish(self):
        super().finish()
        self.__class__.clients.discard(self.request)
        self.broadcast(f"[系统] 用户离开,当前在线 {len(self.clients)} 人")

    def broadcast(self, message):
        for client in list(self.clients):
            try:
                client.sendall((message + '\n').encode('utf-8'))
            except (ConnectionResetError, BrokenPipeError):
                self.clients.discard(client)

if __name__ == '__main__':
    HOST, PORT = '127.0.0.1', 8888
    with socketserver.ThreadingTCPServer((HOST, PORT), ChatHandler) as server:
        print(f"聊天室已启动: {HOST}:{PORT}")
        server.serve_forever()
# client.py - TCP聊天室客户端
import socket
import threading

def receive(sock):
    while True:
        try:
            data = sock.recv(1024).decode('utf-8')
            if data:
                print(data, end='')
        except (ConnectionResetError, OSError):
            print("连接已断开")
            break

def send(sock):
    while True:
        msg = input()
        if msg.lower() == '/quit':
            sock.close()
            break
        sock.sendall((msg + '\n').encode('utf-8'))

if __name__ == '__main__':
    HOST, PORT = '127.0.0.1', 8888
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((HOST, PORT))
    threading.Thread(target=receive, args=(sock,), daemon=True).start()
    send(sock)

运行与验证

  1. 启动服务端:终端执行 python server.py,看到“聊天室已启动”即成功。
  2. 启动多个客户端:另开两个以上终端,分别执行 python client.py。
  3. 验证广播:在任一客户端输入消息并回车,其他所有客户端(包括发送者自己)都应看到 [用户] 消息内容。
  4. 验证上下线通知:新客户端连接时,所有在线客户端收到“新用户加入”提示;关闭一个客户端(输入 /quit 或直接关闭窗口),其余客户端收到“用户离开”提示。
  5. 通过条件:所有客户端能实时收到彼此的广播消息,且上下线通知准确显示在线人数变化,即判定实现成功。

常见问题

  • 端口被占用:若启动服务端报 Address already in use,更换 PORT 或等待系统释放端口。
  • 中文乱码:确保所有终端编码为 UTF-8(Windows 可执行 chcp 65001 后重启终端)。
  • 客户端无法连接:确认服务端已先启动,且 HOST 与 PORT 在两端代码中一致。