Signaling Server — 是WebRTC基础设施的服务器组件,负责在对等端之间交换元数据以建立和终止连接。与媒体流量不同,信令可以通过任何协议传输 — WebSocket、HTTP、XMPP或SIP。根据MDN Web Docs,2024,信令是任何WebRTC应用程序的必需组件,因为该协议未定义交换信令消息的特定方式。
要点
Signaling Server — 是一种网络服务,负责协调两个或多个对等端之间WebRTC连接的建立过程。它不传输媒体数据(音频、视频、DataChannel数据),仅传输对等端发现和连接参数协商所需的服务信息。成功建立P2P通道后,Signaling Server可能不再需要,但在某些架构中,它仍保留用于后续的信号交换(例如,结束通话、添加参与者)。
信令架构包括三个组件:Signaling Server、Signal Channel(客户端和服务器之间的传输协议)和客户端API(通常内置于浏览器的WebRTC栈中)。WebRTC规范(W3C,2024)有意不标准化信令协议 — 开发人员可以选择适合其应用程序的任何传输方式。这种灵活的解决方案允许为Web应用程序使用WebSocket,为聊天系统使用XMPP,或为与电信基础设施集成使用SIP。
在建立WebRTC连接之前,对等端必须交换三种类型的消息:session description(offer和answer)、ICE candidates以及有关会话结束/修改的信息。Signaling Server使用房间或用户标识符进行寻址,在这些对等端之间路由消息。标准模式是创建一个“房间”,两个参与者连接到该房间,服务器将每个参与者的消息仅转发给其对话者。
Signaling Server实现了以下典型的WebRTC连接建立协议。对等端通过WebSocket(或其他传输方式)连接到服务器并在房间中注册。对等端A(发起者)通过RTCPeerConnection.createOffer()创建offer(传出媒体流的SDP描述),将其设置为local description并发送到Signaling Server。服务器将offer转发给对等端B。对等端B接收offer,将其设置为remote description,通过createAnswer()创建answer,将其设置为local description并通过服务器发送回去。这个过程称为SDP Offer/Answer。
在SDP交换的同时,每个对等端收集ICE candidates(host、srflx、relay)并通过Signaling Server发送给另一个对等端。远程对等端通过RTCPeerConnection.addIceCandidate()添加接收到的候选者。ICE过程检查所有候选者组合以找到可行的路径。找到可行的路径后(通常在1-5秒内),媒体流量开始在对等端之间直接传输,Signaling Server不再参与数据传输 — 其角色完成,直到下一个服务事件(结束通话、更改流质量)。
为了消息的寻址,Signaling Server使用房间或通道机制。每个新的WebRTC会话创建一个带有标识符(通常是UUID)的唯一房间。发起者创建房间并等待第二个对等端连接。第二个对等端通过外部通道(例如邀请链接)获取的ID连接到房间。服务器维护一个房间映射,每个ID对应一个已连接客户端的列表。当参与者数量达到两个时,服务器开始在他们之间转发信令消息。
Signaling Server可以使用不同的传输协议,每种协议都有其优点和缺点。协议的选择取决于应用程序类型、基础设施限制和兼容性要求。以下是最常见的协议及其特点。
| 协议 | 传输方式 | 优点 | 缺点 |
|---|---|---|---|
| WebSocket | TCP | 全双工、低延迟、内置于浏览器 | 扩展复杂性、代理阻塞 |
| HTTP/SSE | TCP | 与任何基础设施兼容、实现简单 | 仅单向(服务器到客户端)、需要轮询 |
| XMPP | TCP | 标准化、支持身份验证、可扩展 | 对简单场景过于冗余、XML开销 |
| SIP | UDP/TCP | 与VoIP和电话基础设施集成 | 复杂、非浏览器原生 |
| MQTT | TCP | 轻量、适用于IoT环境、发布/订阅 | 需要代理、额外延迟 |
WebSocket是Web应用程序中Signaling Server最流行的协议。它提供全双工通信,这对于SDP和ICE候选者的异步交换非常重要,并且所有现代浏览器通过WebSocket API原生支持它。WebSocket的服务器实现在所有流行平台上都可用(Node.js、Python、Java、Go)。对于拥有数百万用户的应用程序,使用基于Redis Pub/Sub或Kafka的可扩展WebSocket解决方案来实现Signaling Server实例之间的同步。
让我们看看在Node.js中使用ws库(WebSocket)和内置HTTP服务器的简单Signaling Server实现。该服务器支持用户注册、创建房间和在参与者之间转发消息。
const WebSocket = require("ws");
const server = new WebSocket.Server({ port: 8080 });
const rooms = new Map();
server.on("connection", (ws) => {
ws.roomId = null;
ws.on("message", (data) => {
const msg = JSON.parse(data);
switch (msg.type) {
case "join":
handleJoin(ws, msg.roomId);
break;
case "offer":
case "answer":
case "ice-candidate":
relayToPeer(ws, msg);
break;
case "leave":
handleLeave(ws);
break;
}
});
ws.on("close", () => handleLeave(ws));
});
function handleJoin(ws, roomId) {
if (!rooms.has(roomId)) {
rooms.set(roomId, []);
}
const room = rooms.get(roomId);
room.push(ws);
ws.roomId = roomId;
if (room.length === 2) {
room[0].send(JSON.stringify({ type: "peer-joined" }));
room[1].send(JSON.stringify({ type: "peer-joined" }));
}
}
function relayToPeer(sender, msg) {
const room = rooms.get(sender.roomId);
if (!room) return;
room.forEach(peer => {
if (peer !== sender && peer.readyState === WebSocket.OPEN) {
peer.send(JSON.stringify(msg));
}
});
}
function handleLeave(ws) {
if (!ws.roomId) return;
const room = rooms.get(ws.roomId);
if (!room) return;
const idx = room.indexOf(ws);
if (idx !== -1) room.splice(idx, 1);
if (room.length === 0) rooms.delete(ws.roomId);
}
这个Signaling Server实现了基本功能:连接到房间、在两个对等端之间转发WebRTC消息(offer、answer、ice-candidate)以及管理断开连接。服务器使用Map来存储带有已连接WebSocket客户端的房间。relayToPeer函数将消息发送给房间中除发送者之外的所有参与者。对于生产环境,需要添加消息类型确认、JSON解析错误处理以及用于检测断开连接的心跳机制。
在客户端,Signaling Server通过浏览器的WebSocket API集成。客户端与服务器建立连接,发送加入房间的请求,然后处理传入的WebRTC消息,通过setRemoteDescription()和addIceCandidate()将它们传递给RTCPeerConnection。客户端代码还将其从RTCPeerConnection通过onicecandidate事件以及在创建offer/answer后获得的自己的SDP和ICE候选者发送到服务器。
Signaling Server传输两种关键元数据:SDP(Session Description Protocol)和ICE candidates。SDP描述媒体流的参数 — 编解码器、采样频率、通道数量、传输方向(sendrecv、sendonly、recvonly、inactive)。ICE candidates包含网络地址(本地地址、从STUN获取的地址、从TURN中继的地址),对等端通过这些地址可用于连接。
SDP以包含会话和媒体部分的文本格式呈现。会话部分描述通用参数(会话标识符、版本、名称),媒体部分描述每个媒体流(音频、视频、DataChannel)及其编解码器、端口和协议。ICE候选者包含foundation(用于分组的标识符)、priority、IP地址、端口、类型(host、srflx、relay)和协议(UDP、TCP)。每个候选者还包含ufrag(username fragment)属性,将其与特定的ICE过程关联起来。
Trickle ICE显著加快了WebRTC连接的建立速度。无需等待所有ICE候选者的完整收集(在复杂网络中可能需要2-10秒),每个候选者在被发现后立即发送到Signaling Server。远程对等端接收候选者并立即开始通过ICE框架测试连接。在大多数情况下,这将连接建立时间减少到500-1500毫秒。
常见问题
Signaling Server — 是通话前的“协调员”。它帮助两个设备找到彼此并商定如何通信。在设备“认识”并商定后,服务器不再需要 — 它们直接通信。
WebRTC不定义信令协议,以便开发人员可以选择最合适的传输方式。浏览器没有用于发现其他用户的内置机制 — 这个任务由Signaling Server解决。它充当“邮递员”,在通话参与者之间传递邀请和连接设置。
WebSocket — 大多数Web应用程序的最佳选择:全双工、浏览器原生支持、实现简单。要与现有VoIP基础设施集成,请选择SIP。对于功能丰富的聊天应用程序 — XMPP。对于物联网场景 — MQTT。
要扩展Signaling Server,请使用通过Redis Pub/Sub或Kafka进行同步的水平扩展。每个服务器实例处理自己的那部分WebSocket连接,并使用共享数据总线进行服务器间消息路由。这种方法可以处理数百万个并发信令会话。
Signaling Server仅在连接建立阶段至关重要。如果服务器暂时不可用,活动的WebRTC通话会继续 — 媒体流量直接在对等端之间传输。只有在尝试建立新连接时才会出现问题。为了提高可靠性,请使用服务器集群和备用信令通道。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。