在 mediasoup 中,connect transport 流程是建立 WebRTC 媒体传输通道的核心步骤,主要涉及 ICE(Interactive Connectivity Establishment)参数交换、DTLS(Datagram Transport Layer Security)握手以及 UDP 媒体通道的最终建立。以下是该流程的详细解析,结合具体信令交互和代码逻辑进行说明。
一、信令触发:应用层发起连接请求
连接流程始于应用层(如 Node.js)向 Worker 进程发送 router.connectWebRtcTransport 信令。此信令携带了客户端的 ICE 和 DTLS 参数,用于与服务端协商。
请求信令示例(简化):
json
{
"method": "router.connectWebRtcTransport",
"id": 6,
"data": {
"dtlsParameters": {
"fingerprints": [
{
"algorithm": "sha-256",
"value": "42:F5:D7:5D:F0:96:68:66:B4:F7:B9:0F:13:DA:7F:CE:26:48:94:0E:55:03:C2:B7:FA:1D:0D:ED:EB:10:7D:29"
}
],
"role": "auto"
},
"iceParameters": {
"password": "apfkmkqlij8brze8zqohrd7e3kl0wwj9",
"usernameFragment": "1ga7pj9kdinmeqxb"
}
},
"internal": {
"routerId": "ab8baa18-e002-44d7-a6e2-dc298587dd8b",
"transportId": "253c0380-359c-4102-9dce-c6db3a625944"
}
}
注:此信令通过 libuv 管理的进程间管道(UnixStreamSocket)发送,由 Worker 进程的 Channel::UnixStreamSocket 接收并解析为 Request 对象 。
二、服务端处理:参数验证与状态初始化
Worker 进程收到信令后,会根据 routerId 和 transportId 找到对应的 WebRtcTransport 实例,并调用其 HandleRequest 方法处理连接请求。
核心处理逻辑(C++伪代码):
cpp
// WebRtcTransport::HandleRequest 方法片段
void WebRtcTransport::HandleRequest(Channel::Request* request) {
switch (request->methodId) {
case Channel::Request::MethodId::TRANSPORT_CONNECT: {
// 1. 解析 ICE 远程参数
json& data = request->data;
std::string remoteUfrag = data["iceParameters"]["usernameFragment"];
std::string remotePwd = data["iceParameters"]["password"];
// 设置 ICE 服务器远程凭证
this->iceServer->SetRemoteCredentials(remoteUfrag, remotePwd);
// 2. 确定 ICE 角色(通常服务端为 "controlled")
std::string role = data["dtlsParameters"]["role"];
if (role == "auto") {
this->iceRole = IceRole::CONTROLLED; // 服务端通常为受控方
} else {
this->iceRole = (role == "controlling") ? IceRole::CONTROLLING : IceRole::CONTROLLED;
}
// 3. 设置 DTLS 远程指纹
auto& fingerprints = data["dtlsParameters"]["fingerprints"];
std::vector<DtlsFingerprint> remoteFps;
for (auto& fp : fingerprints) {
remoteFps.push_back({fp["algorithm"], fp["value"]});
}
this->dtlsTransport->SetRemoteFingerprints(remoteFps);
// 4. 更新状态
this->iceState = IceState::CHECKING;
this->dtlsState = DtlsState::CONNECTING;
// 5. 发送成功响应
request->Accept();
break;
}
}
}
处理要点:
- ICE 参数设置 :服务端记录客户端提供的
ice-ufrag和ice-pwd,用于后续 STUN 消息的完整性验证 。 - 角色协商 :ICE 角色决定了连通性检查的发起方。在典型的 mediasoup 部署中,服务端通常作为
controlled方,等待客户端发起检查 。 - DTLS 指纹验证:服务端存储客户端证书的指纹,在 DTLS 握手时用于验证客户端身份,防止中间人攻击 。
三、网络层交互:ICE 连通性检查与 DTLS 握手
信令处理完成后,实际的网络协议交互开始。这是一个异步、事件驱动的过程。
1. ICE 连通性检查:
- 客户端(作为
controlling方)会向服务端发送 STUN Binding Request。 - 服务端的
UdpSocket(libuv 封装)收到 UDP 包后,通过OnUdpSocketPacketReceived回调传递给WebRtcTransport。 WebRtcTransport判断为 STUN 包后,交给IceServer处理。IceServer::ProcessStunPacket()方法验证请求的 MESSAGE-INTEGRITY(使用远程 ICE 密码计算),并生成 STUN Binding Response 返回 。- 当双向的检查都成功,ICE 状态变为
connected。
2. DTLS 握手:
- ICE 连通后,
WebRtcTransport会触发 DTLS 握手。 - 如果服务端 ICE 角色是
controlled,则等待客户端发送 ClientHello。 - 握手过程在 UDP 通道上进行,交换 Certificate、ServerKeyExchange 等消息。
- 服务端用之前设置的远程指纹验证客户端证书。
- 握手成功后,DTLS 状态变为
connected,并导出 SRTP 密钥材料,用于后续媒体流的加密和解密 。
状态转换与事件通知 :
服务端状态变化会通过事件通知应用层。
| 阶段 | 触发条件 | 内部状态变更 | 通知应用层的事件 |
|---|---|---|---|
| ICE 检查开始 | 设置远程 ICE 参数后 | iceState = CHECKING |
icestatechange |
| ICE 连接成功 | 收到有效的 STUN 绑定请求并响应 | iceState = CONNECTED |
icestatechange |
| DTLS 握手开始 | ICE 连接成功后 | dtlsState = CONNECTING |
dtlsstatechange |
| DTLS 连接成功 | DTLS 握手完成且指纹验证通过 | dtlsState = CONNECTED |
dtlsstatechange, connect |
事件通过 Channel::Notifier::Emit() 发送,应用层据此知道传输通道已就绪 。
四、媒体流传输:SRTP 通道建立
DTLS 连接建立后,媒体通道才真正可用。
密钥派生与 SRTP 上下文创建:
cpp
// 握手完成后,DTLS 层导出密钥材料(伪代码)
void DtlsTransport::OnDtlsConnected() {
// 导出 SRTP 密钥(客户端和服务端各导出一次)
ExportKeyingMaterial("SRTP_AES128_CM_SHA1_80", keyingMaterial, 60);
// 创建 SRTP 会话,用于加密/解密 RTP/RTCP 包
srtpSession = new SrtpSession();
srtpSession->SetKey(keyingMaterial); // 设置加密密钥
// 通知上层传输已就绪
listener->OnDtlsConnected(this);
}
媒体包处理流程:
- 接收端 :
UdpSocket收到 UDP 包 ->WebRtcTransport::OnUdpSocketPacketReceived-> 判断为 RTP/RTCP 包 ->DtlsTransport解密 ->SrtpSession解密并验证 -> 根据 SSRC 分发至对应的Producer或Consumer。 - 发送端 :
Producer或Consumer准备 RTP 包 ->SrtpSession加密 ->DtlsTransport发送 ->UdpSocket::Send-> libuv 发送到网络。
至此,一个基于 UDP、受 DTLS 保护的媒体传输通道就完全建立起来了。
五、总结:Connect Transport 的核心步骤
整个 connect transport 流程可以概括为以下关键步骤,其顺序和依赖关系如下表所示:
| 步骤 | 协议层 | 主要动作 | 依赖条件 | 结果/输出 |
|---|---|---|---|---|
| 1. 信令交换 | 应用层 | 客户端发送 ICE/DTLS 参数给服务端 | Transport 已创建 | 服务端获得对端参数 |
| 2. 参数设置 | Transport | 服务端设置远程 ICE 凭证和 DTLS 指纹 | 收到有效的信令 | 准备进行网络协商 |
| 3. ICE 连通 | 网络层 | 交换 STUN 请求/响应,进行连通性检查 | 双方有可行的候选地址 | 建立 UDP 传输路径 |
| 4. DTLS 握手 | 安全层 | 交换证书、协商密码套件、验证指纹 | ICE 通道已连通 | 建立加密上下文,导出 SRTP 密钥 |
| 5. 媒体流启动 | 媒体层 | 使用 SRTP 加密/解密 RTP/RTCP 包 | DTLS 握手完成 | 音视频数据开始安全传输 |
这个过程充分体现了 WebRTC 的"建立连接后再通信"原则,通过 ICE 解决 NAT 穿透问题,通过 DTLS 提供通信安全,最终在可靠的 UDP 通道上传输媒体数据。整个流程由信令驱动初始化,由网络事件驱动执行,是 mediasoup 实现高并发、低延迟媒体传输的基础 。