import asyncio
import websockets
import sys
from datetime import datetime
# 全局客户端连接池
connected = set()
async def broadcast(message: str):
"""广播消息到所有连接的客户端"""
if connected: # 仅当有客户端连接时执行
print(f"[BROADCAST] {message}")
# 遍历副本防止迭代时集合被修改
for ws in list(connected):
try:
await ws.send(message)
except websockets.exceptions.ConnectionClosed:
# 移除断开的客户端
connected.remove(ws)
async def terminal_input_handler():
"""终端输入监听协程"""
while True:
# 阻塞等待终端输入(按回车发送)
user_input = await asyncio.to_thread(sys.stdin.readline)
if user_input.strip().lower() == 'exit':
print("[SERVER] 接收到退出指令,正在关闭...")
break
# 格式化消息(带时间戳)
timestamp = datetime.now().strftime("%H:%M:%S")
message = f"{user_input.strip()}"
# 执行广播
await broadcast(message)
async def handle_client(websocket):
"""处理客户端连接"""
print(f"[CONNECTION] 新客户端接入: {websocket.remote_address}")
connected.add(websocket)
try:
# 保持连接(客户端不主动发消息时,仅接收广播)
await websocket.wait_closed()
finally:
connected.remove(websocket)
print(f"[DISCONNECTION] 客户端断开: {websocket.remote_address}")
async def main():
# 启动 WebSocket 服务器(监听 8765 端口)
async with websockets.serve(handle_client, "0.0.0.0", 8765):
# 启动终端输入监听
input_task = asyncio.create_task(terminal_input_handler())
# 等待任务完成(输入 exit 或 Ctrl+C 退出)
try:
await input_task
except KeyboardInterrupt:
print("\n[SERVER] 接收到中断信号,正在关闭...")
if __name__ == "__main__":
print("=== WebSocket 广播服务器启动 ===")
print("终端输入消息后按回车即可广播,输入 'exit' 退出")
print("客户端连接地址: ws://你的IP:8765")
asyncio.run(main())