在 Mediasoup 中WebRtcTransport的创建流程是一个涉及客户端与服务器端信令交互、资源初始化的关键过程它是建立 WebRTC 媒体传输通道的基础。其创建流程可以概括为客户端发起请求 - 服务器端Node.js 层接收并处理 - 转发至 C Worker 进程创建实际传输实例 - 返回连接信息给客户端。以下将详细拆解每个步骤并提供核心代码示例。一、 创建流程详细步骤整个流程是信令驱动的下图概述了从客户端调用createWebRtcTransport到获得transportOptions的主要步骤序列sequenceDiagram participant C as 客户端 participant N as Node.js服务器 participant W as C Worker进程 C-N: 发送信令: createWebRtcTransport Note over N: 1. 验证权限与参数 N-W: 调用worker.createWebRtcTransport() Note over W: 2. 创建内部Transport对象br3. 生成ICE参数及DTLS证书 W-N: 返回内部Transport信息 Note over N: 4. 构建client所需的transportOptions N-C: 响应transportOptionsbr(含id, iceParameters, dtlsParameters等)步骤 1客户端发起创建请求客户端通常基于mediasoup-client库通过信令通道WebSocket向服务器发送一个信令请求请求创建一个新的WebRtcTransport。这个请求通常包含一个forceTcp标志指示是否强制使用 TCP 候选者等可选参数 。步骤 2Node.js 服务器层处理Node.js 应用层如mediasoup-demo中的room.js收到createWebRtcTransport信令。权限与状态校验检查用户是否在房间内是否已存在 transport 等 。参数准备组装创建WebRtcTransport所需的选项 (options)主要包括listenIps: 指定服务器监听的 IP 地址列表用于生成 ICE 候选者。enableUdp/enableTcp: 是否启用 UDP/TCP 传输。preferUdp/preferTcp: 候选者优先级偏好。initialAvailableOutgoingBitrate: 初始可用出口带宽。appData: 自定义应用数据 。调用 Worker通过worker.createWebRtcTransport(options)方法将创建请求下发到对应的 C Worker 子进程 。步骤 3C Worker 进程创建核心对象这是最核心的一步发生在mediasoup的 C 子进程中。创建WebRtcTransport实例Worker 进程根据传入的options实例化一个WebRtcTransport对象。该对象内部会管理 ICE、DTLS、SRTP 等协议栈 。生成 ICE 参数根据listenIps通过libnice或libwebrtc的 ICE 栈收集主机、服务器反射如果配置了 STUN 服务器和中继如果配置了 TURN 服务器候选者。生成iceParameters包括usernameFragment和password用于 ICE 连通性检查 。生成 DTLS 参数创建或复用 DTLS 证书和指纹 (dtlsParameters)用于后续的 DTLS 握手建立加密通道 。初始化内部状态初始化RtpSender、RtpReceiver等组件准备处理 RTP/RTCP 数据流 。步骤 4信息返回与客户端配置构造响应C Worker 将创建好的WebRtcTransport的内部信息ID、ICE 参数、DTLS 参数、ICE 候选者列表等返回给 Node.js 层。Node.js 层封装Node.js 层接收这些信息并封装成一个transportOptions对象准备发送给客户端。客户端接收与配置客户端收到transportOptions后使用这些参数配置本地的RTCPeerConnection并通过addIceCandidate()添加 ICE 候选者从而启动 ICE 连接建立过程 。二、 核心代码示例以下代码示例基于mediasoup-demo的常见模式展示了服务器端Node.js和客户端的关键代码片段。1. 服务器端 Node.js 代码示例// 文件server/room.js (示例) const mediasoup require(mediasoup); async function createWebRtcTransport(socket, roomId, forceTcp false) { const room rooms.get(roomId); const consumerPeer room.getPeer(socket.id); if (!consumerPeer) { throw new Error(Peer ${socket.id} not found in room ${roomId}); } // 步骤 2: 准备 WebRtcTransport 配置选项 const transportOptions { listenIps: [ { ip: 0.0.0.0, // 监听所有 IPv4 地址 announcedIp: 192.168.1.100 // 对外宣布的 IP如果是公网 IP 或域名 } ], enableUdp: !forceTcp, // 是否启用 UDP enableTcp: true, // 启用 TCP 作为备选 preferUdp: true, // 优先使用 UDP preferTcp: false, // TCP 优先级较低 initialAvailableOutgoingBitrate: 300000, // 初始出口带宽 300kbps appData: { peerId: socket.id } // 自定义数据 }; // 步骤 2 4: 调用 Worker 创建 Transport 并获取结果 const transport await room.worker.createWebRtcTransport(transportOptions); // // 将 transport 与 peer 关联存储 consumerPeer.transports.set(transport.id, transport); // 监听 transport 的 ‘icestatechange’ 和 ‘dtlsstatechange’ 事件以监控连接状态 transport.on(icestatechange, (iceState) { console.log(Transport ${transport.id} ICE state changed to: ${iceState}); }); transport.on(dtlsstatechange, (dtlsState) { console.log(Transport ${transport.id} DTLS state changed to: ${dtlsState}); if (dtlsState connected) { console.log(DTLS handshake completed for transport ${transport.id}); } }); // 步骤 4: 构建给客户端的响应数据 const response { id: transport.id, iceParameters: transport.iceParameters, // ICE 用户名片段和密码 iceCandidates: transport.iceCandidates, // ICE 候选者列表 dtlsParameters: transport.dtlsParameters, // DTLS 指纹和角色 sctpParameters: transport.sctpParameters // 如果启用 SCTP用于 DataChannel }; return response; // 此 response 将通过信令发送给客户端 } // 信令处理部分如使用 socket.io socket.on(createWebRtcTransport, async ({ forceTcp }, callback) { try { const transportOptions await createWebRtcTransport(socket, roomId, forceTcp); callback({ transportOptions }); // 将 transportOptions 发送回客户端 } catch (error) { callback({ error: error.message }); } });2. 客户端 JavaScript 代码示例// 文件client.js (示例使用 mediasoup-client) import * as mediasoupClient from mediasoup-client; let device null; let sendTransport null; // 1. 加载设备Device async function loadDevice(routerRtpCapabilities) { device new mediasoupClient.Device(); await device.load({ routerRtpCapabilities }); // 加载服务器的 RTP 能力 } // 2. 创建发送 Transport基于服务器返回的 transportOptions async function createSendTransport(transportOptionsFromServer) { // transportOptionsFromServer 即服务器端 createWebRtcTransport 返回的对象 sendTransport device.createSendTransport(transportOptionsFromServer); // // 监听 Transport 事件这些是必须实现的回调函数 sendTransport.on(connect, async ({ dtlsParameters }, callback, errback) { // 当 Transport 需要连接时即启动 DTLS 握手通知服务器 try { // 通过信令发送 connectWebRtcTransport 请求携带 dtlsParameters await socket.emitWithAck(connectWebRtcTransport, { transportId: sendTransport.id, dtlsParameters }); callback(); // 连接成功通知底层库 } catch (error) { errback(error); // 连接失败通知底层库 } }); sendTransport.on(produce, async ({ kind, rtpParameters, appData }, callback, errback) { // 当调用 transport.produce() 开始发送媒体时触发 try { // 通过信令通知服务器创建一个新的 Producer const { id } await socket.emitWithAck(produce, { transportId: sendTransport.id, kind, rtpParameters, appData }); callback({ id }); // 将服务器生成的 Producer ID 返回给底层库 } catch (error) { errback(error); } }); sendTransport.on(connectionstatechange, (state) { console.log(Send Transport connection state: ${state}); }); return sendTransport; } // 3. 主流程接收服务器信令并创建 Transport socket.on(newTransportOptions, async ({ transportOptions }) { // 假设服务器通过 newTransportOptions 事件下发 transportOptions await createSendTransport(transportOptions); console.log(Send WebRtcTransport created successfully.); }); // 触发流程首先加载设备然后请求创建 Transport async function init() { // 从服务器获取 routerRtpCapabilities const routerRtpCapabilities await getRouterRtpCapabilitiesFromServer(); await loadDevice(routerRtpCapabilities); // 发送信令请求创建 WebRtcTransport socket.emit(createWebRtcTransport, { forceTcp: false }, (response) { if (response.error) { console.error(Failed to create transport:, response.error); return; } // 客户端收到 transportOptions触发上面的 newTransportOptions 监听器 socket.emit(newTransportOptions, { transportOptions: response.transportOptions }); }); }三、 关键点与内部机制ICE 候选者收集与优先级listenIps配置直接影响候选者类型。announcedIp在服务器位于 NAT 后或需要暴露公网 IP 时至关重要。候选者优先级计算遵循 RFC 5245通常hostsrflx(server-reflexive) relay。DTLS 角色协商在connect阶段客户端需要将本地的dtlsParameters包含指纹和支持的密码套件发送给服务器服务器端的transport.connect()方法会完成 DTLS 角色client/server的最终确定和握手 。多进程模型创建操作最终在 C Worker 进程执行这隔离了 CPU 密集型的媒体处理任务提升了 Node.js 主进程的 I/O 响应能力 。资源管理每个WebRtcTransport都关联着底层网络套接字和协议状态机。在transport.close()被调用时这些资源会被妥善释放 。总结WebRtcTransport的创建流程是 Mediasoup 信令交互的核心之一它搭建了 ICE 和 DTLS 通道的骨架。客户端与服务器通过交换iceParameters、iceCandidates和dtlsParameters协同工作为后续的produce发送媒体和consume接收媒体奠定安全的传输基础 。理解此流程对于调试连接问题如 ICE 失败、DTLS 握手失败至关重要。参考来源mediasoupdemo官方信令服务代码分析dataChannel的使用深入浅出mediasoup—WebRtcTransport二、mediasoup之WebRtcTransport创建流程1mediasoup create/connect WebRtcTransport 流程分析mediasoup-初始化、建立连接及媒体数据的处理流程mediasoup源码分析-初始化、建立连接及媒体数据的处理流程