初學者編寫的多用戶聊天程序雖然能建立連接,但出現「後連接者擠掉先連接者」且「消息無法在客戶端間轉發」的情況。核心原因通常是:**服務端未使用多線程/多進程/IO 多路復用並發處理多個客戶端**,導致主循環阻塞在單個連接上。
服務端主線程
├─ 監聽端口 (socket.bind/listen)
└─ 循環 accept 客戶端連接
├─ 為每個客戶端創建獨立線程/協程
│ ├─ 接收消息循環
│ └─ 廣播消息給所有其他在線客戶端
└─ 維護在線客戶端列表 (需線程安全)
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()