mediasoup中connect transport详解

在 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 进程收到信令后,会根据 routerIdtransportId 找到对应的 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;
        }
    }
}

处理要点

  1. ICE 参数设置 :服务端记录客户端提供的 ice-ufragice-pwd,用于后续 STUN 消息的完整性验证 。
  2. 角色协商 :ICE 角色决定了连通性检查的发起方。在典型的 mediasoup 部署中,服务端通常作为 controlled 方,等待客户端发起检查 。
  3. 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);
}

媒体包处理流程

  1. 接收端UdpSocket 收到 UDP 包 -> WebRtcTransport::OnUdpSocketPacketReceived -> 判断为 RTP/RTCP 包 -> DtlsTransport 解密 -> SrtpSession 解密并验证 -> 根据 SSRC 分发至对应的 ProducerConsumer
  2. 发送端ProducerConsumer 准备 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 实现高并发、低延迟媒体传输的基础 。


参考来源

相关推荐
REDcker14 小时前
QUIC协议系列导读
音视频·webrtc·实时音视频·webtransport
烟雨江南78514 小时前
跨通道回声消除与离线ASR流式转写的物理级对齐:基于Kaldi与WebRTC Audio Processing的深度重构实践
人工智能·webrtc·语音识别·ai质检
metaRTC16 小时前
metaRTC8 freertos编程指南(bk7258/bk7259)
音视频·webrtc·rtos
REDcker3 天前
QUIC应用实践
音视频·webrtc·实时音视频·webtransport
深念Y3 天前
我明白为什么B站没法在浏览器开直播了——Windows Chrome推流踩坑全记录
前端·chrome·webrtc·浏览器·srs·直播·flv
Fisher3Star3 天前
Mediasoup SVC分层传输实现解析
webrtc
深念Y4 天前
仿B站直播功能技术选型:为什么必须用SRS而不是WebRTC P2P?
webrtc·srs·直播·推流·b站·多媒体·obs
REDcker4 天前
QUIC协议详解1
音视频·webrtc·实时音视频·webtransport
许彰午5 天前
WebRTC只管流不管控——自研信令服务器的状态机设计
运维·服务器·webrtc