深色模式
WebSocket
Node.js 云函数支持 WebSocket 长连接,可用于实时通知和协作等场景。
创建 WebSocket 端点
创建一个以 .node.js 结尾的文件,例如 ws.node.js。这个文件既是云函数文件,也是 WebSocket 的连接路径:
text
wss://example.com/ws.node.jsWebSocket 使用网站本身的域名、标准端口和文件路径,不需要额外监听端口,也没有必须使用的固定路径。网址中需要保留 .node.js 后缀。
云函数后端代码
WebSocket 连接中会提供全局的 websocket 对象。
js
if (websocket) {
websocket.on("open", () => {
websocket.send("连接成功");
});
websocket.on("message", (data) => {
websocket.send(`收到消息:${data}`);
});
websocket.on("close", (code, reason) => {
console.log(`连接已关闭:${code} ${reason}`);
});
websocket.on("error", (error) => {
console.log(error.message);
});
} else {
res.status(426);
document.write("请使用 WebSocket 连接");
}同一个 .node.js 文件仍可通过普通 HTTP 请求访问。普通 HTTP 请求中,websocket 的值为 null,可以像上面的示例一样分别处理两种请求。
网页前端代码
浏览器使用标准的 WebSocket 接口连接:
js
const socket = new WebSocket("/ws.node.js");
socket.addEventListener("message", (event) => {
console.log(event.data);
});
socket.addEventListener("open", () => {
socket.send("Hello");
});跨连接发送
使用 websocket.sendToAll() 可以向同一域名、同一 WebSocket 端点中的所有已连接客户端发送消息,包含当前连接。例如,在新连接建立时更新所有客户端显示的在线人数:
js
if (websocket) {
websocket.on("open", async () => {
const peerIds = await websocket.getPeers();
const notification = JSON.stringify({
onlineCount: peerIds.length + 1,
type: "presence",
});
websocket.sendToAll(notification);
});
}如果只需要向个别客户端发送,可以使用 websocket.getPeers() 获取其他已连接客户端的 ID,并通过 websocket.sendTo() 向指定客户端发送。例如,在新连接建立时只与第一个已在线的客户端配对,并通知双方:
js
if (websocket) {
websocket.on("open", async () => {
const peerIds = await websocket.getPeers();
const partnerId = peerIds[0];
if (!partnerId) {
websocket.send(JSON.stringify({ type: "waiting" }));
return;
}
websocket.send(
JSON.stringify({ partnerId: partnerId, type: "matched" }),
);
websocket.sendTo(
partnerId,
JSON.stringify({ partnerId: websocket.id, type: "matched" }),
);
});
}getPeers() 会在调用时查询当前开启的其他连接,不包含当前连接、正在关闭或已经关闭的连接。如果连接在发送时关闭,sendTo() 和 sendToAll() 会忽略该连接。
完整示例
下面的示例会显示当前页面的在线连接数,并在连接建立或断开时实时更新所有客户端。
js
if (websocket) {
websocket.on("open", async () => {
const peerIds = await websocket.getPeers();
const notification = JSON.stringify({
onlineCount: peerIds.length + 1,
type: "presence",
});
websocket.sendToAll(notification);
});
websocket.on("close", async () => {
const peerIds = await websocket.getPeers();
const notification = JSON.stringify({
onlineCount: peerIds.length,
type: "presence",
});
websocket.sendToAll(notification);
});
websocket.on("error", (error) => {
console.error(error.message);
});
} else {
res.status(426);
document.write("请使用 WebSocket 连接");
}html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>实时在线状态</title>
</head>
<body>
<h1>实时在线状态</h1>
<p id="status">正在连接</p>
<p>当前在线:<strong id="online-count">0</strong></p>
<script>
const socket = new WebSocket("/presence.node.js");
socket.addEventListener("open", () => {
document.getElementById("status").textContent = "已连接";
});
socket.addEventListener("message", (event) => {
const data = JSON.parse(event.data);
document.getElementById("online-count").textContent = data.onlineCount;
});
socket.addEventListener("close", () => {
document.getElementById("status").textContent = "连接已断开";
document.getElementById("online-count").textContent = "0";
});
</script>
</body>
</html>接口
事件
websocket.on("open", listener):连接建立后触发。websocket.on("message", (data) => {}):收到文本消息时触发,data是字符串。websocket.on("close", (code, reason) => {}):连接关闭后触发。websocket.on("error", (error) => {}):连接发生错误时触发。
事件监听函数中的 this 指向当前 websocket 对象。监听函数可以是异步函数,返回的 Promise 会在本次事件结束前被等待。如需在事件之间共享和保留状态,请使用内置的数据库。
方法
websocket.getPeers():异步返回同一域名、同一 WebSocket 端点中其他已打开连接的 ID 数组,需要使用await。websocket.send(data):向当前连接发送字符串。websocket.sendTo(peerId, data):向指定的已打开连接发送字符串;连接不存在或已经关闭时不发送。websocket.sendToAll(data):向同一域名、同一 WebSocket 端点中所有已打开的连接发送字符串,包含当前连接。websocket.close(code?, reason?):关闭当前连接。状态码和原因均可省略。
属性和状态
websocket.id:当前连接的唯一标识。websocket.protocol:协商后的子协议;未使用子协议时为空字符串。websocket.readyState:当前连接状态。websocket.CONNECTING、websocket.OPEN、websocket.CLOSING、websocket.CLOSED:可与readyState比较的状态常量。