首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Python Socket 多用戶聊天室:解決連接擁擠與消息轉發問題

     編輯:程式設計綜合問答

問題現象

初學者編寫的多用戶聊天程序雖然能建立連接,但出現「後連接者擠掉先連接者」且「消息無法在客戶端間轉發」的情況。核心原因通常是:**服務端未使用多線程/多進程/IO 多路復用並發處理多個客戶端**,導致主循環阻塞在單個連接上。

正確架構設計

服務端主線程
  ├─ 監聽端口 (socket.bind/listen)
  └─ 循環 accept 客戶端連接
       ├─ 為每個客戶端創建獨立線程/協程
       │    ├─ 接收消息循環
       │    └─ 廣播消息給所有其他在線客戶端
       └─ 維護在線客戶端列表 (需線程安全)

基於 threading 的完整服務端示例

import socket
import threading

HOST = '0.0.0.0'
PORT = 9999
clients = []  # 存儲 (conn, addr)
clients_lock = threading.Lock()

def broadcast(msg, sender_conn):
    with clients_lock:
        for conn, _ in clients:
            if conn != sender_conn:
                try:
                    conn.send(msg)
                except:
                    pass  # 忽略發送失敗

def handle_client(conn, addr):
    print(f'[新連接] {addr} 已上線')
    with clients_lock:
        clients.append((conn, addr))
    try:
        while True:
            data = conn.recv(1024)
            if not data:
                break
            print(f'[{addr}] {data.decode()}')
            broadcast(f'[{addr[1]}] {data.decode()}'.encode(), conn)
    finally:
        with clients_lock:
            clients.remove((conn, addr))
        conn.close()
        print(f'[斷開] {addr} 下線')

def main():
    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()
        threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()

if __name__ == '__main__':
    main()

客戶端示例

import socket
import threading

def recv_loop(sock):
    while True:
        try:
            data = sock.recv(1024)
            if not data: break
            print('\n' + data.decode() + '\n> ', end='')
        except:
            break

def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(('127.0.0.1', 9999))
    threading.Thread(target=recv_loop, args=(s,), daemon=True).start()
    print('已連接,輸入消息按 Enter 發送 (輸入 quit 退出)')
    while True:
        msg = input('> ')
        if msg == 'quit': break
        s.send(msg.encode())
    s.close()

if __name__ == '__main__':
    main()

關鍵點總結

  • 每連接一線程:`threading.Thread` 處理單客戶端收發,主線程只負責 `accept`。
  • 線程安全列表:用 `Lock` 保護 `clients` 列表的增刪改查。
  • 廣播機制:收到消息遍歷列表發送給除發送者外的所有連接。
  • 優雅斷開:`recv` 返回空字節表示對端關閉,及時清理資源。

進階優化方向

  1. IO 多路復用:使用 `selectors` / `asyncio` 替代線程,支撐萬級並發(C10K 問題)。
  2. 消息協議:定義長度前綴或分隔符,解決 TCP 粘包/拆包導致消息截斷或粘連。
  3. 心跳檢測:定期發送 Ping 包,清理僵死連接。
  4. 用戶身份:加入登錄/昵稱系統,替代 IP:Port 顯示。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved