4、信令服务器搭建:WebSocket基础、Node.js信令服务、移动端信令交互
信令服务器,说白了就是WebRTC的“通信中介”。
我刚开始接触WebRTC时,以为只要两端建立连接就能直接传数据。后来才发现,双方必须先交换一些“元信息”——比如对方在哪、用什么编码、网络情况如何。这些信息,就得靠信令服务器来传递。
嗯,这一章我们就来亲手搭一个信令服务器。
4.1 WebSocket基础
WebRTC的信令传输,最常用的底层协议就是WebSocket。
为什么选它?因为WebSocket是全双工的,服务器可以主动推消息给客户端。这在信令交互中非常关键——你想想看,当A用户发起呼叫,服务器得立刻通知B用户,而不是等B来轮询。
核心概念:WebSocket 建立后,连接会一直保持,直到某一方主动关闭。这比HTTP的“请求-响应”模式更适合实时通信。
一个简单的WebSocket连接,客户端代码长这样:
// 客户端(移动端或浏览器)
const socket = new WebSocket('ws://192.168.1.100:8080');
socket.onopen = () => {
console.log('连接已建立');
socket.send(JSON.stringify({ type: 'join', room: 'room1' }));
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
};
socket.onclose = () => {
console.log('连接已关闭');
};
我在项目中遇到过一个问题:移动端网络切换时(比如从WiFi切到4G),WebSocket会断开。如果不做重连机制,信令就断了,通话直接挂掉。所以后来我习惯在客户端加一个心跳和自动重连逻辑。
避坑指南:我曾经在iOS上遇到WebSocket莫名其妙断开,查了半天发现是后台进程被系统挂起了。解决方案是在App进入后台前,主动发送一个ping帧保持连接活跃。
4.2 Node.js信令服务
服务端我选Node.js,原因很简单:事件驱动、非阻塞I/O,天然适合处理大量并发连接。而且WebSocket的库生态非常成熟。
我个人习惯用 ws 这个库,轻量、稳定。安装就一行命令:
npm install ws
一个最基本的信令服务器,核心逻辑就是“转发”——把A的消息转发给B。但前提是,你得知道B是谁、在哪个房间。
下面是一个简化版的信令服务器代码:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
// 存储房间和用户映射
const rooms = new Map();
wss.on('connection', (ws) => {
let currentRoom = null;
let userId = null;
ws.on('message', (message) => {
const data = JSON.parse(message);
switch (data.type) {
case 'join':
// 用户加入房间
currentRoom = data.room;
userId = data.userId;
if (!rooms.has(currentRoom)) {
rooms.set(currentRoom, new Map());
}
rooms.get(currentRoom).set(userId, ws);
console.log(`${userId} 加入了房间 ${currentRoom}`);
break;
case 'offer':
case 'answer':
case 'ice-candidate':
// 转发给房间内的其他用户
const room = rooms.get(currentRoom);
if (room) {
room.forEach((client, id) => {
if (id !== userId) {
client.send(JSON.stringify({
type: data.type,
from: userId,
payload: data.payload
}));
}
});
}
break;
default:
console.log('未知消息类型:', data.type);
}
});
ws.on('close', () => {
// 用户离开时清理
if (currentRoom && rooms.has(currentRoom)) {
rooms.get(currentRoom).delete(userId);
if (rooms.get(currentRoom).size === 0) {
rooms.delete(currentRoom);
}
}
console.log(`${userId} 离开了`);
});
});
console.log('信令服务器已启动,端口: 8080');
这段代码虽然简单,但已经覆盖了信令的核心流程:加入房间、转发offer/answer、转发ICE候选者。
注意:生产环境中,你还需要考虑身份认证、消息校验、房间容量限制等。我曾经因为没做消息校验,被恶意客户端伪造offer消息,导致其他用户频繁崩溃。
4.3 移动端信令交互
移动端(iOS/Android)与信令服务器的交互,本质上和浏览器端一样,都是通过WebSocket收发JSON消息。但有几个细节需要特别注意。
先看一个典型的信令交互流程:
// 移动端发起呼叫的伪代码
1. 连接WebSocket
2. 发送 join 消息,加入房间
3. 创建 PeerConnection
4. 创建 Offer(本地SDP)
5. 通过WebSocket发送 offer 消息
6. 等待 answer 消息
7. 收到 answer 后,设置远端SDP
8. 收到 ICE候选者,添加到 PeerConnection
在iOS上,我常用 Starscream 这个库。Android上则用 OkHttp 的WebSocket支持。代码逻辑大同小异。
下面是一个iOS端的WebSocket连接示例(Swift):
import Starscream
class SignalingClient: WebSocketDelegate {
var socket: WebSocket?
func connect() {
var request = URLRequest(url: URL(string: "ws://192.168.1.100:8080")!)
request.timeoutInterval = 5
socket = WebSocket(request: request)
socket?.delegate = self
socket?.connect()
}
func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .connected(let headers):
print("连接成功: \(headers)")
// 加入房间
sendMessage(["type": "join", "room": "room1", "userId": "ios_user_1"])
case .text(let text):
// 处理收到的信令消息
handleSignalingMessage(text)
case .disconnected(let reason, let code):
print("断开连接: \(reason), code: \(code)")
default:
break
}
}
func sendMessage(_ dict: [String: Any]) {
if let data = try? JSONSerialization.data(withJSONObject: dict),
let str = String(data: data, encoding: .utf8) {
socket?.write(string: str)
}
}
}
Android端(Kotlin)类似:
// 使用 OkHttp
val client = OkHttpClient()
val request = Request.Builder().url("ws://192.168.1.100:8080").build()
val ws = client.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
// 加入房间
val joinMsg = """{"type":"join","room":"room1","userId":"android_user_1"}"""
webSocket.send(joinMsg)
}
override fun onMessage(webSocket: WebSocket, text: String) {
// 处理信令消息
handleSignalingMessage(text)
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
// 重连逻辑
}
})
经验之谈:移动端网络环境复杂,我建议在信令交互中加入消息确认机制。比如A发送offer后,服务器回复一个ack。如果A没收到ack,就重发。我曾经因为丢包导致offer没到达B,两边干等了10秒才发现。
4.4 信令交互流程图
下面我用一张SVG图,把整个信令交互过程串起来。你看完应该能一目了然。
你看,信令服务器在整个过程中只做一件事:转发。它不碰媒体数据,也不参与编解码。所以即使服务器性能一般,只要带宽够,也能支撑大量并发信令。
4.5 小结
这一章我们做了三件事:
- 理解了WebSocket的基本用法,以及为什么它适合信令传输
- 用Node.js搭建了一个可用的信令服务器,核心逻辑就是“转发”
- 看了移动端(iOS/Android)如何接入信令服务器,以及需要注意的重连、确认机制
信令服务器是WebRTC应用的“神经系统”。它不直接传输音视频,但少了它,两端根本找不到对方。我见过不少项目在信令上翻车——要么丢消息、要么并发处理不当导致崩溃。所以这一块,值得你花时间打磨。
一句话总结:信令服务器 = WebSocket + 消息转发 + 房间管理。别想复杂了,先把这三样做好,后面再慢慢加认证、加密、日志。
公众号:蓝海资料掘金营,微信deep3321