WebRTC 實戰 (7) —— 信令機制的正式封裝與房間系統
上一篇使用 PeerJS 快速實現連線。本篇則回到自建信令路線:當應用需要大廳、房間列表、權限或多人狀態時,必須由應用伺服器管理房間成員與訊息路由。
這一篇我們將回歸伺服器端,探討如何設計一個具備 「房間 (Room)」 概念的正式信令架構。
一、 房間系統的邏輯抽象
在社交或會議軟體中,使用者通常加入房間,而不是手動輸入對方的 Peer ID。信令伺服器必須維護房間成員、驗證訊息接收者,並在連線中斷時清理狀態。
二、 設計擴展性的信令協定
1. 指令規範清單
- join:使用者加入房間。
- peer-joined:通知房間內其他使用者有新人加入。
- offer / answer / candidate:沿用前幾篇的信令格式並轉送
payload。 - leave / peer-left:處理使用者主動離開或意外斷線。
三、 實作:房間制信令伺服器 (Node.js)
javascript
const WebSocket = require("ws");
const wss = new WebSocket.Server({ port: 8080 });
const rooms = new Map(); // roomId -> Set<userId>
const clients = new Map(); // userId -> WebSocket
function sendTo(userId, message) {
const target = clients.get(userId);
if (target?.readyState === WebSocket.OPEN) {
target.send(JSON.stringify(message));
}
}
function leaveRoom(ws) {
const { roomId, userId } = ws;
if (!roomId || !userId) return;
const members = rooms.get(roomId);
members?.delete(userId);
clients.delete(userId);
members?.forEach((memberId) => {
sendTo(memberId, { type: "peer-left", from: userId });
});
if (members?.size === 0) rooms.delete(roomId);
}
wss.on('connection', (ws) => {
ws.on('message', (msg) => {
let message;
try {
message = JSON.parse(msg.toString());
} catch {
ws.send(JSON.stringify({ type: "error", payload: "Invalid JSON" }));
return;
}
const { type, roomId, from, to, payload } = message;
switch (type) {
case 'join':
if (!roomId || !from || clients.has(from)) return;
ws.userId = from;
ws.roomId = roomId;
clients.set(from, ws);
if (!rooms.has(roomId)) rooms.set(roomId, new Set());
rooms.get(roomId).forEach((memberId) => {
sendTo(memberId, { type: "peer-joined", from });
});
rooms.get(roomId).add(from);
break;
case 'offer':
case 'answer':
case 'candidate':
if (from !== ws.userId) return;
if (!rooms.get(ws.roomId)?.has(to)) return;
sendTo(to, { type, from, to, payload });
break;
case 'leave':
leaveRoom(ws);
break;
}
});
ws.on('close', () => leaveRoom(ws));
});這個版本補上了接收者索引、同房驗證與斷線清理,但仍是教學骨架。正式服務還需要登入驗證、房間權限、訊息結構驗證、心跳、速率限制,以及多節點部署時的共享狀態或訊息代理。
總結
透過建立房間的概念,我們成功解耦了使用者 ID 與連線邏輯,並設計了一套標準的信令協定,為具備產品級水準的通訊軟體打下基礎。
下一章,我們將學習如何使用 WebRTC-Internals 工具剖析通訊中的數據細節。
進階挑戰
試著在你的信令伺服器增加「密碼房間」功能。當使用者發起 join 時,必須帶上正確的 password 參數,否則伺服器會回傳一個 JOIN_DENIED 的訊息。
延伸閱讀與資源
- Socket.io: Rooms and Namespaces
- WebRTC Samples: Signaling Concepts