利用FastAPI开发WebSocket接口

WebSocket 是基于 HTTP 升级的长连接协议,浏览器和前端都支持得很好。

HTTP vs WebSocket

HTTP(传统请求响应)

  • 浏览器:发一个请求 → 服务器:回一个响应 → 连接就结束了(短连接)。
  • 如果要持续拿数据,就要不停地“轮询”:隔几秒再发一次 HTTP 请求。

WebSocket

  • 一开始也是通过 HTTP 发起请求,但带上 Upgrade: websocket 头,请求“升级协议”。
  • 服务器同意后,这条连接就从“普通 HTTP”升级为 WebSocket 长连接。
  • 之后这条连接会一直保持(直到双方主动断开或异常断开)。客户端可以随时发消息给服务端。服务端也可以主动推消息给客户端(不像 HTTP 必须“等请求”)。

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
"""
FastAPI 混合接口示例
"""
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from datetime import datetime

app = FastAPI(title="混合接口示例")


# ===== HTTP 接口 =====

@app.get("/health")
async def health():
"""健康检查"""
return {"status": "ok", "time": datetime.now().isoformat()}


@app.get("/api/info")
async def get_info():
"""获取信息"""
return {"name": "测试服务", "version": "1.0"}


@app.post("/api/echo")
async def echo(data: dict):
"""回显POST数据"""
return {"received": data}


# ===== WebSocket 接口 =====

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
"""WebSocket 回显"""
await websocket.accept()
print("WebSocket 已连接")

try:
while True:
data = await websocket.receive_text()
print(f"收到: {data}")
await websocket.send_text(f"回复: {data}")
except WebSocketDisconnect:
print("WebSocket 已断开")


if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8080)

客户端(脚本)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"""测试 WebSocket 连接"""
import asyncio
import websockets

async def test():
uri = "ws://localhost:8080/ws"
print(f"连接到 {uri}")

async with websockets.connect(uri) as ws:
print("连接成功!")

# 发送消息
await ws.send("你好")
print("已发送: 你好")

# 接收回复
response = await ws.recv()
print(f"收到回复: {response}")

asyncio.run(test())

客户端(网页)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html>
<head>
<title>WebSocket 测试</title>
<style>
body { font-family: Arial; padding: 20px; }
#log { background: #f0f0f0; padding: 10px; height: 200px; overflow-y: auto; margin: 10px 0; }
input { padding: 8px; width: 300px; }
button { padding: 8px 16px; margin-left: 5px; }
</style>
</head>
<body>
<h2>WebSocket 测试</h2>
<div>
<button onclick="connect()">连接</button>
<button onclick="disconnect()">断开</button>
</div>
<div id="log"></div>
<div>
<input type="text" id="msg" placeholder="输入消息" value="你好">
<button onclick="send()">发送</button>
</div>

<script>
let ws = null;
const log = (msg) => {
document.getElementById('log').innerHTML += msg + '<br>';
};

function connect() {
ws = new WebSocket('ws://localhost:8080/ws');
ws.onopen = () => log('✅ 已连接');
ws.onmessage = (e) => log('📩 收到: ' + e.data);
ws.onclose = () => log('❌ 已断开');
ws.onerror = (e) => log('⚠️ 错误');
}

function disconnect() {
if (ws) ws.close();
}

function send() {
const msg = document.getElementById('msg').value;
if (ws && ws.readyState === 1) {
ws.send(msg);
log('📤 发送: ' + msg);
} else {
log('⚠️ 未连接');
}
}
</script>
</body>
</html>