WebSocket 协议支持
本文介绍如何在 Web 云函数中实现 WebSocket 长连接,支持服务端与客户端之间的双向实时通信。
什么是 WebSocket
「WebSocket」是一种在单个 TCP 连接上进行全双工通信的协议,允许服务端主动向客户端推送数据。与传统的 HTTP 请求-响应模式不同,WebSocket 建立连接后可以保持长连接,实现服务端和客户端之间的实时双向数据传输。
主要特点:
- 全双工通信:服务端和客户端可以同时发送和接收消息
- 持久连接:建立连接后保持长连接,无需重复握手
- 低延迟:消息传递实时高效,适合实时应用场景
- 协议切换:通过 HTTP Upgrade 机制从 HTTP 协议升级为 WebSocket 协议
典型应用场景:
- 实时聊天应用
- 在线协作编辑
- 实时数据监控
- 游戏服务器
- 实时通知推送
工作原理
服务启动
在支持 WebSocket 的 Web 云函数中,通过启动文件启动 WebSocket 服务器,服务器必须在 9000 端口 上监听。
建立连接
- 客户端通过
ws://或wss://协议向云函数发起连接请求 - 云函数平台将连接透传给运行环境中的 WebSocket 服务进程(9000 端口)
- 连接协商及后续通信完全由您的 WebSocket 服务端代码处理
连接生命周期
- 连接与实例映射:一次 WebSocket 连接的生命周期等同于一次函数调用请求
- 连接建立 = 请求发起
- 连接断开 = 请求结束
- 实例映射:函数实例与连接是一一对应的,同一实例在某一时刻仅处理一个 WebSocket 连接,新连接会启动新的实例
- 连接保持:连接建立后,实例持续运行,处理双向数据传输
- 连接结束:当 WebSocket 连接断开时,对应的函数实例停止运行
使用限制
在使用 WebSocket 时,需要注意以下配额限制:
| 限制项 | 说明 |
|---|---|
| 空闲超时时间 | 10 ~ 7200 秒,连接上无消息传输时的超时时间 |
| 执行超时时间 | 必须 ≥ 空闲超时时间,函数的最大运行时长 |
| 单次消息大小 | 最大 256KB |
| 单连接传输速率 | 最大 128KB/s |
| 单连接请求 QPS | 最大 10 次/秒 |
连接断开与状态码
不同的断开情况对应不同的函数状态码:
| 断开情况 | 函数表现 | 函数状态码 |
|---|---|---|
| 正常关闭 状态码为 1000、1010、1011 | 函数正常执行结束 | 200 |
| 异常关闭 非标准关闭状态码 | 函数异常结束 | 439 (服务端关闭) 456 (客户端关闭) |
| 空闲超时 连接无消息传输超过配置时间 | 函数异常结束 | 455 |
| 执行超时 连接持续时间超过函数最大运行时长 | 函数异常结束 | 433 |
操作步骤
步骤1:开启 WebSocket 协议支持
在创建或编辑云函数时,需要在控制台开启 WebSocket 协议支持。
- 登录 云开发平台/云函数/函数列表
- 点击「新建云函数」或选择已有函数点击「编辑」
- 在「函数配置」中找到「WebSocket 协议」配置项

- 开启「WebSocket 协议」开关
- 设置「WebSocket 空闲超时时间」(范围:10-7200 秒)
- 确保「执行超时时间」≥「空闲超时时间」
- 点击「保存」完成配置
⚠️ 注意:WebSocket 协议一旦开启后不可取消,但空闲超时时间可以后续修改。
步骤2:安装依赖并编写服务端代码
根据您使用的编程语言,安装对应的 WebSocket 库并编写服务端代码。WebSocket 服务器必须监听 9000 端口。
- Node.js
- Python
安装依赖
安装 ws 库:
npm install ws
在 package.json 中添加依赖:
{
"dependencies": {
"ws": "^8.0.0"
}
}
编写服务端代码
使用 ws 库实现 WebSocket 服务器:
const WebSocket = require('ws');
// 创建 WebSocket 服务器,必须监听 9000 端口
const wss = new WebSocket.Server({ port: 9000 });
wss.on('connection', (ws) => {
console.log('新连接建立');
// 发送欢迎消息
ws.send('欢迎连接到 WebSocket 服务');
// 接收消息
ws.on('message', (message) => {
console.log('收到消息:', message.toString());
// 回复消息
const response = {
type: 'response',
data: message.toString(),
timestamp: Date.now(),
};
ws.send(JSON.stringify(response));
});
// 连接关闭
ws.on('close', () => {
console.log('连接已关闭');
});
// 错误处理
ws.on('error', (error) => {
console.error('WebSocket 错误:', error);
});
});
console.log('WebSocket 服务器已启动,监听端口 9000');
安装依赖
在 requirements.txt 中添加 websockets 库:
websockets
编写服务端代码
使用 websockets 库实现 WebSocket 服务器:
import asyncio
import websockets
import json
from datetime import datetime
async def handle_connection(websocket, path):
"""处理 WebSocket 连接"""
try:
# 发送欢迎消息
await websocket.send("欢迎连接到 WebSocket 服务")
# 持续接收消息
async for message in websocket:
print(f"收到消息: {message}")
# 回复消息
response = {
"type": "response",
"data": message,
"timestamp": datetime.now().isoformat()
}
await websocket.send(json.dumps(response))
except websockets.exceptions.ConnectionClosed:
print("连接已关闭")
except Exception as e:
print(f"错误: {e}")
# 启动 WebSocket 服务
async def main():
# 必须监听 9000 端口
async with websockets.serve(handle_connection, "0.0.0.0", 9000):
await asyncio.Future() # 保持运行
if __name__ == "__main__":
asyncio.run(main())
步骤3:部署云函数
完成代码编写后,部署云函数:
- 在控制台点击「部署」按钮
- 或使用 CloudBase CLI:
tcb fn deploy --httpFn
步骤4:客户端连接测试
使用浏览器或 Node.js 客户端连接 WebSocket 服务:
// 浏览器端
const ws = new WebSocket('wss://your-function.run.tcloudbase.com');
ws.onopen = () => {
console.log('连接已建立');
ws.send('Hello, server!');
};
ws.onmessage = (event) => {
console.log('收到消息:', event.data);
};
ws.onclose = () => {
console.log('连接已关闭');
};
ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
高级用法
1. 广播消息
将消息发送给所有连接的客户端:
- Node.js
- Python
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 9000 });
// 存储所有连接的客户端
const clients = new Set();
wss.on('connection', (ws) => {
// 添加到客户端列表
clients.add(ws);
console.log(`新连接建立,当前连接数: ${clients.size}`);
ws.on('message', (message) => {
const msg = message.toString();
console.log('收到消息:', msg);
// 广播给所有客户端
clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(
JSON.stringify({
type: 'broadcast',
data: msg,
timestamp: Date.now(),
})
);
}
});
});
ws.on('close', () => {
// 移除断开的客户端
clients.delete(ws);
console.log(`连接已关闭,当前连接数: ${clients.size}`);
});
});
import asyncio
import websockets
import json
from datetime import datetime
# 存储所有连接的客户端
clients = set()
async def handle_connection(websocket, path):
# 添加到客户端列表
clients.add(websocket)
print(f"新连接建立,当前连接数: {len(clients)}")
try:
async for message in websocket:
print(f"收到消息: {message}")
# 广播给所有客户端
disconnected = set()
for client in clients:
try:
await client.send(json.dumps({
"type": "broadcast",
"data": message,
"timestamp": datetime.now().isoformat()
}))
except websockets.exceptions.ConnectionClosed:
disconnected.add(client)
# 移除已断开的客户端
clients.difference_update(disconnected)
finally:
# 移除断开的客户端
clients.remove(websocket)
print(f"连接已关闭,当前连接数: {len(clients)}")
2. 心跳检测
实现心跳机制保持连接活跃:
- Node.js
- Python
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 9000 });
wss.on('connection', (ws) => {
let isAlive = true;
// 心跳检测:每 30 秒发送 ping
const heartbeatInterval = setInterval(() => {
if (!isAlive) {
console.log('心跳超时,关闭连接');
ws.terminate();
return;
}
isAlive = false;
ws.ping();
}, 30000);
// 收到 pong 响应
ws.on('pong', () => {
isAlive = true;
});
ws.on('message', (message) => {
console.log('收到消息:', message.toString());
ws.send(`回复: ${message.toString()}`);
});
ws.on('close', () => {
console.log('连接已关闭');
clearInterval(heartbeatInterval);
});
});
import asyncio
import websockets
async def handle_connection(websocket, path):
try:
# 启动心跳任务
heartbeat_task = asyncio.create_task(send_heartbeat(websocket))
async for message in websocket:
print(f"收到消息: {message}")
await websocket.send(f"回复: {message}")
finally:
heartbeat_task.cancel()
print("连接已关闭")
async def send_heartbeat(websocket):
"""每 30 秒发送心跳"""
try:
while True:
await asyncio.sleep(30)
await websocket.ping()
except asyncio.CancelledError:
pass
3. 房间管理
实现简单的房间功能,支持多个客户端加入不同房间进行分组通信:
- Node.js
- Python
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 9000 });
// 房间管理
const rooms = new Map();
wss.on('connection', (ws) => {
let currentRoom = null;
ws.on('message', (message) => {
try {
const data = JSON.parse(message.toString());
// 加入房间
if (data.action === 'join') {
currentRoom = data.room;
if (!rooms.has(currentRoom)) {
rooms.set(currentRoom, new Set());
}
rooms.get(currentRoom).add(ws);
ws.send(
JSON.stringify({
type: 'system',
message: `已加入房间: ${currentRoom}`,
roomSize: rooms.get(currentRoom).size,
})
);
}
// 发送消息到房间
if (data.action === 'message' && currentRoom) {
const roomClients = rooms.get(currentRoom);
roomClients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(
JSON.stringify({
type: 'message',
room: currentRoom,
content: data.content,
timestamp: Date.now(),
})
);
}
});
}
} catch (e) {
console.error('消息处理错误:', e);
}
});
ws.on('close', () => {
// 离开房间
if (currentRoom && rooms.has(currentRoom)) {
rooms.get(currentRoom).delete(ws);
// 清理空房间
if (rooms.get(currentRoom).size === 0) {
rooms.delete(currentRoom);
}
}
});
});
import asyncio
import websockets
import json
from datetime import datetime
# 房间管理
rooms = {}
async def handle_connection(websocket, path):
current_room = None
try:
async for message in websocket:
try:
data = json.loads(message)
# 加入房间
if data.get('action') == 'join':
current_room = data.get('room')
if current_room not in rooms:
rooms[current_room] = set()
rooms[current_room].add(websocket)
await websocket.send(json.dumps({
"type": "system",
"message": f"已加入房间: {current_room}",
"roomSize": len(rooms[current_room])
}))
# 发送消息到房间
if data.get('action') == 'message' and current_room:
room_clients = rooms.get(current_room, set())
disconnected = set()
for client in room_clients:
try:
await client.send(json.dumps({
"type": "message",
"room": current_room,
"content": data.get('content'),
"timestamp": datetime.now().isoformat()
}))
except websockets.exceptions.ConnectionClosed:
disconnected.add(client)
# 移除已断开的客户端
room_clients.difference_update(disconnected)
except json.JSONDecodeError:
print("消息解析错误")
finally:
# 离开房间
if current_room and current_room in rooms:
rooms[current_room].discard(websocket)
# 清理空房间
if len(rooms[current_room]) == 0:
del rooms[current_room]
客户端连接示例
- 浏览器
- Node.js 客户端
// 连接 WebSocket
const ws = new WebSocket('wss://your-service.run.tcloudbase.com');
// 连接建立
ws.onopen = () => {
console.log('WebSocket 连接已建立');
ws.send('Hello, server!');
};
// 接收消息
ws.onmessage = (event) => {
console.log('收到消息:', event.data);
try {
const data = JSON.parse(event.data);
console.log('解析的数据:', data);
} catch (e) {
// 处理非 JSON 消息
}
};
// 连接关闭
ws.onclose = () => {
console.log('WebSocket 连接已关闭');
};
// 错误处理
ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
// 发送消息
function sendMessage(msg) {
if (ws.readyState === WebSocket.OPEN) {
ws.send(msg);
}
}
const WebSocket = require('ws');
const ws = new WebSocket('wss://your-service.run.tcloudbase.com');
ws.on('open', () => {
console.log('连接已建立');
ws.send('Hello, server!');
});
ws.on('message', (data) => {
console.log('收到消息:', data.toString());
});
ws.on('close', () => {
console.log('连接已关闭');
});
ws.on('error', (error) => {
console.error('错误:', error);
});
常见问题
1. 连接无法建立怎么办?
- 检查是否在控制台开启了 WebSocket 协议支持
- 确认 WebSocket 服务器正在监听 9000 端口
- 确认使用正确的 WebSocket 地址(
ws://或wss://) - 检查云函数是否正常部署和运行
- 查看云函数日志是否有错误信息
2. 连接频繁断开怎么办?
- 检查空闲超时时间配置是否合理(建议设置较大值,如 3600 秒)
- 实现心跳机制保持连接活跃(每 30 秒发送一次 ping/pong)
- 检查客户端网络是否稳定
- 查看云函数日志,确认是否有异常错误导致连接断开
- 确保执行超时时间 ≥ 空闲超时时间
3. 如何实现多人聊天室?
参考本文「高级用法 - 房间管理」章节,使用 Map 或 Set 存储房间和连接信息。对于大规模应用,建议:
- 使用 Redis 等外部存储管理连接状态和房间信息
- 实现连接池管理
- 考虑使用消息队列处理广播消息
4. 如何处理大量并发连接?
- 实例扩展:云函数会自动为每个 WebSocket 连接创建独立实例
- 状态管理:使用 Redis 等外部存储共享连接状态
- 消息队列:使用消息队列(如 TDMQ)处理广播和异步消息
- 负载均衡:多个云函数实例自动负载均衡
- 资源监控:关注函数实例数量和资源使用情况
5. 9000 端口被占用怎么办?
云函数运行环境中,9000 端口是专门为 WebSocket 保留的,不会被其他服务占用。如果本地开发时遇到端口占用:
# 查找占用 9000 端口的进程
lsof -i :9000
# 终止进程(Mac/Linux)
kill -9 <PID>
6. 如何实现断线重连?
客户端实现自动重连机制:
class ReconnectingWebSocket {
constructor(url, maxRetries = 5) {
this.url = url;
this.maxRetries = maxRetries;
this.retries = 0;
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('连接已建立');
this.retries = 0; // 重置重试计数
};
this.ws.onclose = () => {
console.log('连接已关闭');
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket 错误:', error);
};
this.ws.onmessage = (event) => {
console.log('收到消息:', event.data);
};
}
reconnect() {
if (this.retries < this.maxRetries) {
this.retries++;
const delay = Math.min(1000 * Math.pow(2, this.retries), 30000);
console.log(`${delay}ms 后尝试重连 (${this.retries}/${this.maxRetries})`);
setTimeout(() => {
this.connect();
}, delay);
} else {
console.error('达到最大重试次数,停止重连');
}
}
send(data) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data);
} else {
console.warn('连接未打开,无法发送消息');
}
}
}
// 使用
const ws = new ReconnectingWebSocket('wss://your-function.run.tcloudbase.com');