Python WebSocket 编程
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,允许客户端和服务器之间实时、双向、持久的通信。它基于 HTTP 协议,通过一次握手(HTTP Upgrade 请求)建立连接,随后双方可以随时发送数据,无需像 HTTP 那样每次都发起新请求。WebSocket 适用于需要低延迟、实时交互的应用,如聊天室、实时通知、在线游戏等。
核心特点:
- 全双工:客户端和服务器可同时发送和接收消息。
- 持久连接:连接建立后保持开放,直到一方主动关闭。
- 低开销:相比 HTTP 的轮询,WebSocket 减少了协议头和连接建立的开销。
- 事件驱动:通过事件(如 onopen, onmessage, onclose, onerror)处理连接状态和数据。
编程要点:
- 握手:客户端发起 WebSocket 请求,服务器响应后建立连接。
- 消息处理:监听消息事件,处理文本或二进制数据。
- 连接管理:处理连接断开、错误重试和心跳机制以保持连接活跃。
- 协议:URL 使用 ws:// 或 wss://(加密),支持子协议(如 chat 或 json)。
fastapi 编写聊天室
编写服务端代码 server.py
服务端安装
pip install fastapi "uvicorn[standard]"
服务端代码
import asyncio
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
import logging
app = FastAPI()
# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("chat_server")
# 存储已连接的客户端和用户名
connected_clients = {}
usernames = set()
async def broadcast_message(message: str):
"""向所有已连接的客户端广播消息"""
for connection in connected_clients.values():
await connection.send_text(message)
@app.get("/", response_class=HTMLResponse)
async def get_chat_page():
"""提供聊天室网页"""
with open("index.html") as f:
return f.read()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
username = websocket.query_params.get('username')
if not username:
await websocket.send_text("Username is required.")
await websocket.close()
return
if username in usernames:
await websocket.send_text("Username already taken.")
await websocket.close()
return
usernames.add(username)
connected_clients[username] = websocket
await websocket.send_text(f"Welcome, {username}!")
await broadcast_message(f"System: {username} has joined the chat")
try:
while True:
message = await websocket.receive_text()
await broadcast_message(f"{username}: {message}")
except WebSocketDisconnect:
del connected_clients[username]
usernames.remove(username)
await broadcast_message(f"System: {username} has left the chat")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
客户端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Chat Room</title>
<style>
#chat-area {
height: 300px;
overflow-y: scroll;
border: 1px solid #ccc;
padding: 10px;
}
</style>
</head>
<body>
<div>
<input type="text" id="username" placeholder="Username">
<button id="connect">Connect</button>
</div>
<div id="chat-area"></div>
<div>
<input type="text" id="message" placeholder="Type a message" disabled>
<button id="send" disabled>Send</button>
</div>
<script>
const usernameInput = document.getElementById('username');
const connectButton = document.getElementById('connect');
const messageInput = document.getElementById('message');
const sendButton = document.getElementById('send');
const chatArea = document.getElementById('chat-area');
let ws;
let connected = false;
connectButton.addEventListener('click', () => {
const username = usernameInput.value.trim();
if (!username) {
alert('Please enter a username');
return;
}
connectButton.disabled = true;
const wsUrl = `ws://127.0.0.1:8000/ws?username=${encodeURIComponent(username)}`;
ws = new WebSocket(wsUrl);
ws.onopen = () => {
// 等待欢迎消息
};
ws.onmessage = (event) => {
const message = event.data;
if (message === "Username already taken.") {
alert('Username already taken. Please choose another.');
connectButton.disabled = false;
} else if (message.startsWith("Welcome, ")) {
connected = true;
messageInput.disabled = false;
sendButton.disabled = false;
appendMessage(message);
} else {
appendMessage(message);
}
};
ws.onclose = () => {
if (connected) {
appendMessage("Disconnected from chat");
}
connected = false;
messageInput.disabled = true;
sendButton.disabled = true;
connectButton.disabled = false;
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
});
sendButton.addEventListener('click', () => {
const message = messageInput.value.trim();
if (message && connected) {
ws.send(message);
messageInput.value = '';
}
});
function appendMessage(message) {
const p = document.createElement('p');
p.textContent = message;
chatArea.appendChild(p);
chatArea.scrollTop = chatArea.scrollHeight;
}
</script>
</body>
</html>
聊天室使用方法
- 先运行服务端 server.py 确保8000端口正常监听
- 浏览器打开 client.html ,输入用户名点击 connect,然后就可以发送消息了
yuziyue加入聊天

后来 easy 用户也加入了聊天
