webrtc-rs 0.21:Sans-I/O 核心之上的异步 WebRTC 重写
项目速览:Rust 中异步友好的 WebRTC 实现。 async webrtc crate 是在 Sans-I/O 核心之上进行的干净、符合人体工程学、与运行时无关的重写;它附带 Tokio 和 smol 运行时后端,并且可以通过实现一种特征来插入任何其他运行时。
秒懂
- 它是什么?
- webrtc-rs 将 WebRTC 协议栈拆成无 I/O 的 rtc 核心与薄薄的异步壳层,用 Rust 重写了 Pion 的思路。本文拆解它的 Runtime 抽象、加密提供者机制,以及 0.21 预发布版里值得注意的取舍。
- 适合谁用?
- webrtc-rs 0.21 适合那些需要在一个进程里同时服务多种异步运行时、或者想自己控制加密后端的 Rust 项目。它把协议逻辑和 I/O 彻底分开,运行时和加密提供者都可以按连接注入,这是它区别于大多数 WebRTC 库的地方。
- 能商用吗?
- 可以。Apache-2.0 是宽松许可证:你可以使用、修改并销售基于它的软件,只需保留版权和许可证声明。
- 还在维护吗?
- 在维护。仓库最近一次提交在 11 天前。
- 用什么语言写的?
- 主要是 Rust(依据 GitHub 的语言统计)。
以上回答依据项目的 GitHub 数据(最近同步于 2026年9月15日)和我们的分析,不构成法律意见。
开源项目深度解析
它解决什么问题
Rust 里做 WebRTC,以前的选择不多。要么绑定 C 库,要么用一个把协议和 I/O 缠在一起的实现。webrtc-rs 想改变这一点。它把 WebRTC 的完整协议栈,包括 SDP、ICE、DTLS、SRTP 这些,都放进了一个叫 rtc 的 Sans-I/O 核心。这个核心不碰网络,不碰定时器,只处理字节流和状态机。外面再包一层薄薄的异步壳,也就是这个 webrtc crate,负责把异步事件变成核心能吃的输入,再把核心的输出变成回调。这样做的直接好处是,运行时不再被焊死在代码里。你可以在一个进程里同时用 Tokio 和 smol,甚至用别的运行时,只要实现一个 trait。目标用户很明确:需要把 WebRTC 集成进现有 Rust 异步服务的人,尤其是那些不想被某个运行时绑死的项目。
Sans-I/O 核心与异步壳层的分工
这个架构的关键在分层。rtc 核心实现了 95% 以上的 W3C API 语义,但它是纯同步的,不产生任何 I/O。webrtc crate 提供两个主要角色。PeerConnection 是用户看到的异步句柄,创建 offer、添加 track、打开 data channel 都是 async 方法。PeerConnectionDriver 是内部的后台事件循环,它拥有 socket,驱动 rtc 核心,处理超时,分发事件。这个 driver 在 build 时自动启动,用户通常不需要直接碰它。Runtime trait 抽象了定时器、任务生成和 socket,这是运行时无关性的基石。这种设计的代价是,你写代码时要理解事件循环的节奏。比如,ICE 候选的收集不是同步返回的,而是通过 on_ice_candidate 回调异步到达。对习惯传统 WebRTC 回调风格的人来说,这不算陌生,但如果你期待一个同步的 API,这里会有学习曲线。
运行时与加密提供者如何按连接注入
webrtc crate 提供两个层面的可插拔性。运行时方面,默认启用 runtime-tokio,也可以开 runtime-smol,两个 feature 是加性的,可以同时启用。更灵活的是,你完全可以不依赖这两个内置运行时,自己实现 webrtc::runtime::Runtime,然后用 with_runtime 按连接传入。README 里给了 custom-runtime 示例,它用 async-executor 和 async-io 跑通了完整栈,而且编译时关掉了默认 feature,也就是说连 Tokio 和 smol 都没编进去。加密方面,默认用 ring,也可以换成 aws-lc-rs,或者自己实现 crypto::RTCCryptoProvider。这个 trait 可以通过 SettingEngineBuilder 的 with_crypto_provider 方法按连接设置。文档特别强调,crypto 的 feature 也是加性的,启用两个编译两个,但 ring 仍然是默认选择,所以依赖不会悄悄改变你的加密后端。这种设计让一个进程里的不同连接可以用不同 provider,比如一个用 FIPS 验证的模块,另一个用 HSM。
从零跑通的最小步骤
要开始用,先要在 Cargo.toml 里写对版本号。因为 0.21 是预发布版,Cargo 不会从 "0.21" 这个要求里自动选预发布版本,必须写全,比如 webrtc = "0.21.0-alpha.1"。然后实现 PeerConnectionEventHandler trait,处理 on_ice_candidate 这类事件。接着用 PeerConnectionBuilder 和 RTCConfigurationBuilder 构建连接,加上 ICE server,调用 build() 得到 PeerConnection。这个 build() 返回的是不透明的 impl PeerConnection,而不是一个具体类型。如果你要把连接存进结构体或者跨任务共享,就得包成 Arc<dyn PeerConnection>。文档里特别提到,这样做的好处是你的类型里不会泄漏运行时或拦截器的泛型参数。创建 offer 的流程是异步的,结果通过回调拿。整个过程不需要手动 spawn driver,它会在 build 时自动启动。
预发布版与版本选择的坑
0.21 目前处于 beta 阶段,最近一次提交是 2026 年 8 月 22 日,发布的是 v0.21.0-beta.2。这个版本号本身就是个信号:API 还在调整。alpha 和 beta 之间隔了不到一周,说明开发节奏很快,但也意味着破坏性变更可能随时出现。文档明确警告 Cargo 不会自动选预发布版本,这既是好事也是坏事。好事是你不会意外升级到不稳定的版本,坏事是你必须手动追踪版本号,而且如果某个依赖间接要求 webrtc = "0.21",它可能拿不到预发布版,导致解析失败。另一个要注意的是,runtime-mock feature 提供了一个确定性的虚拟时钟,用于测试,但它不做任何 I/O。如果你的测试依赖真实的网络行为,这个 mock 帮不上忙。
它不适合什么场景
webrtc-rs 的架构优雅,但它不是万能的。首先,如果你的应用只需要一个简单的 WebRTC 连接,不想关心运行时抽象和加密提供者,这个库的层次可能会显得过度设计。其次,Sans-I/O 核心意味着你必须在异步壳层里处理所有事件,调试时得同时理解两层。文档没有提供性能基准,所以如果你追求极致吞吐,需要自己验证。另外,crypto 提供者的 conformance 套件只验证 RFC 向量,不保证你自研的 provider 在生产环境下的安全性。如果你不是加密专家,实现 RTCCryptoProvider 的风险很高。最后,这个库的维护活跃度看起来不错,但 0.21 还没稳定,生产环境采用前要评估你能否跟上 API 变化。
与 Pion 的对比:重写而非移植
README 明确说,这个项目最初受 Pion 启发,并且很大程度上重写了 Pion 的栈。Pion 是 Go 写的,它的设计是同步的,用 goroutine 处理并发。webrtc-rs 不是简单翻译,而是把 Pion 的架构改成了 Rust 的异步模型。最明显的区别是 Sans-I/O 核心。Pion 的代码里,协议逻辑和网络 I/O 是交织的,而 webrtc-rs 把 I/O 完全剥离开。这让测试更容易,因为你可以用 mock 运行时驱动核心,而不需要真实的网络。但这也意味着,如果你从 Pion 迁移过来,你不能直接照搬调用方式。Pion 的 PeerConnection 方法大多是同步的,而这里全是 async。另外,Pion 的拦截器机制是内置的,webrtc-rs 的文档没有详细说明拦截器如何工作,只提到 examples 里有 simulcast 和 insertable streams。如果你依赖 Pion 的特定拦截器行为,需要仔细检查 webrtc-rs 是否覆盖。
维护与升级成本
这个项目采用 Apache-2.0 双许可(README 提到了 dual MIT/Apache 的链接,但仓库显示 Apache-2.0),这意味着你可以自由使用,但要留意依赖的许可。维护方面,最近一次提交是 2026 年 8 月,说明项目还活着,但 0.21 的频繁发布也暗示 API 不稳定。升级成本主要来自两个方面。一是运行时抽象,如果你实现了自己的 Runtime trait,那么 trait 的任何变更都会影响你的代码。二是加密提供者,如果你实现了 RTCCryptoProvider,rtc-crypto 的 conformance 套件会帮你验证,但升级时可能需要重新跑。文档没有提供迁移指南,所以升级前最好查看 changelog。对于长期项目,建议锁定版本号,并定期检查是否有新的 beta 或稳定版。
编辑结论
webrtc-rs 0.21 适合那些需要在一个进程里同时服务多种异步运行时、或者想自己控制加密后端的 Rust 项目。它把协议逻辑和 I/O 彻底分开,运行时和加密提供者都可以按连接注入,这是它区别于大多数 WebRTC 库的地方。但要注意,0.21 还是预发布版本,Cargo 不会自动选取,必须写全版本号。如果你只想快速跑通一个 demo,或者团队里没人愿意碰 Sans-I/O 的事件循环,那这个库的抽象层可能会让你觉得绕。采用之前,先确认你的目标运行时是否在 runtime-tokio 和 runtime-smol 之外,如果是,务必跑一遍 custom-runtime 示例,验证你实现的 Runtime trait 能处理真实的网络事件。还要检查你的加密需求,如果必须用 FIPS 模块或 HSM,就得自己实现 crypto::RTCCryptoProvider,并用 rtc-crypto 的 conformance 套件验证。最后,0.21 的 API 还在变,beta 版本之间的差异可能不小,升级时要留意 changelog。
社区笔记