第 8 章:C 语言双雄:msquic 与 ngtcp2——内核级 vs 万能适配
从两种建筑方式说起
想象你在城市里看到两种建筑:
msquic 大楼(微软总部):
- 巨大的自有建筑群,从地基到屋顶全自建
- 有自己的水电暖系统(不依赖市政)
- 电梯从 B2(内核驱动)直达 50 层(用户态应用)
- 门卫安保极严格(fuzzing、形式化验证)
- 大楼里有 Windows Server、Xbox、Teams 等所有微软产品
ngtcp2 大楼(社区共建):
- 规模适中、结构清晰的开源办公楼
- 水电暖系统可以接市政(OpenSSL),也可以接私人管道(GnuTLS/wolfSSL/BoringSSL)
- 地基是另一家公司(nghttp2)的成功经验
- 设计为”万能接口”——任何 TLS 库都能接入
- 开发者是一个人(Tatsuhiro Tsujikawa),质量极高
两栋楼都用 C 语言建造(为了最大兼容性),但一栋追求”自己搞定一切”(msquic),另一栋追求”适配一切环境”(ngtcp2)。
msquic:微软的 QUIC 统一引擎
基本信息
项目:msquic
GitHub: microsoft/msquic
Stars: ~4.2K
语言:C
定位:Windows + Linux 跨平台生产 QUIC
架构:分层设计 + 内核态支持
代码量:~700+ 文件(C/C++ 混合)
TLS 后端:Schannel (Windows) / OpenSSL (Linux)
维护者:微软网络团队 (Nick Banks 等)
msquic 的使命
msquic 不是一个实验项目——它是微软所有 QUIC 需求的统一引擎:
msquic 的部署场景:
1. Windows 内核(kernel mode)
- Windows Server 2022+ 内置 QUIC 支持
- SMB over QUIC(文件共享走 QUIC)
- HTTP.sys(Windows 内置 HTTP 服务器)
2. 用户态应用
- .NET(ASP.NET Core 的 Kestrel 服务器)
- Microsoft Teams
- Xbox 游戏网络
- Azure 各服务
3. Linux 服务器
- Azure 的 CDN 和负载均衡器
- 跨平台微服务
一套代码 → 内核态 + 用户态 + Windows + Linux + 嵌入式
msquic 的分层架构
┌─────────────────────────────────────────────────────────────────┐
│ 应用层 │
│ .NET / C++ / Rust (via FFI) / PowerShell │
├─────────────────────────────────────────────────────────────────┤
│ msquic API 层 │
│ MsQuicOpen → Registration → Configuration → Listener/Connection │
├─────────────────────────────────────────────────────────────────┤
│ 协议核心层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 连接管理 │ │ 流管理 │ │ 丢包恢复 │ │ 拥塞控制 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ TLS 抽象层 │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ Schannel │ │ OpenSSL │ │ 自定义 TLS │ │
│ │ (Windows) │ │ (Linux) │ │ (测试用) │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ 平台抽象层 (PAL) │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ Windows User │ │ Windows Kernel │ │ Linux │ │
│ │ Mode │ │ Mode │ │ (epoll) │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
msquic 的 API 设计:对象模型
msquic 使用一种独特的”对象层次”API 设计:
// msquic 的使用流程
// 1. 打开库(获取函数表)
const QUIC_API_TABLE* MsQuic = NULL;
MsQuicOpen2(&MsQuic);
// 2. 注册应用
HQUIC Registration;
MsQuic->RegistrationOpen(&RegConfig, &Registration);
// 3. 创建配置(安全参数 + 传输参数)
HQUIC Configuration;
MsQuic->ConfigurationOpen(Registration, Alpn, AlpnCount,
&Settings, SettingsSize, NULL,
&Configuration);
MsQuic->ConfigurationLoadCredential(Configuration, &CredConfig);
// 4. 创建监听器(服务端)或连接(客户端)
HQUIC Listener;
MsQuic->ListenerOpen(Registration, ListenerCallback, NULL, &Listener);
MsQuic->ListenerStart(Listener, Alpn, AlpnCount, &Address);
// 5. 在回调中处理事件
QUIC_STATUS ListenerCallback(
HQUIC Listener, void* Context,
QUIC_LISTENER_EVENT* Event
) {
switch (Event->Type) {
case QUIC_LISTENER_EVENT_NEW_CONNECTION:
// 新连接到达
MsQuic->ConnectionSetConfiguration(
Event->NEW_CONNECTION.Connection, Configuration);
break;
}
return QUIC_STATUS_SUCCESS;
}
关键特点:事件驱动 + 回调模型。msquic 不使用 sans-io 模式——它内部管理所有 I/O,通过回调通知应用层发生了什么。
为什么 msquic 不用 sans-io?
msquic 的设计决策:
sans-io 适合的场景:
- 库开发者想让用户自由选择 I/O 模型
- 主要面向 Rust/Python 等有强异步生态的语言
msquic 的场景不同:
- 需要在 Windows 内核态运行(内核没有"你来做 I/O"的自由)
- 需要统一管理线程池(内核态线程是共享资源)
- 需要在各种平台上提供一致的行为
- 性能优化需要控制 I/O 路径(比如 io_uring on Linux)
所以 msquic 选择了"我全包"模式:
- 内部管理线程池
- 内部管理 socket
- 内部管理 I/O 完成通知
- 通过回调通知应用层
好处:应用代码极简,性能优化集中
坏处:灵活性低,不能自定义 I/O 模型
msquic 的内核态支持
msquic 最独特的能力:它可以在 Windows 内核态运行。这意味着什么?
用户态 QUIC(普通模式):
应用层 → 系统调用 → 内核收包 → 拷贝到用户空间 → 解密处理
内核态 QUIC(msquic kernel mode):
内核直接收包 → 解密处理 → 零拷贝传递给应用
用户态 │ 内核态
│
┌──────────┐ ┌──────────┐ │ ┌──────────┐
│ 应用程序 │ │ msquic │ │ │ msquic │
│ │◄──►│ (user) │ │ │ (kernel) │
└──────────┘ └──────────┘ │ └────┬─────┘
│ │ │
▼ │ ▼
┌─────────────┐ │ ┌─────────────┐
│ kernel │ │ │ NIC 驱动 │
│ (syscall) │ │ │ (直接访问) │
└─────────────┘ │ └─────────────┘
│
延迟:~10μs 系统调用开销 │ 延迟:~0μs(同在内核)
吞吐:受限于用户/内核拷贝 │ 吞吐:零拷贝,极高
使用场景:
- SMB over QUIC(Windows 文件共享)
- HTTP.sys(IIS/Kestrel 的内核 HTTP 栈)
- Azure 负载均衡器(每秒百万连接级别)
msquic 的安全保障
作为微软的核心网络库,msquic 有极其严格的安全要求:
安全措施:
1. 持续 Fuzzing
- OSS-Fuzz 集成(Google 的持续模糊测试服务)
- 每天数十亿次随机输入测试
- 覆盖所有解析路径
2. 静态分析
- SAL 注解(Microsoft 的静态注解语言)
- 每个指针标注 _In_, _Out_, _In_opt_ 等
- 编译器自动检查缓冲区溢出风险
3. 内存安全
- 自定义内存池(避免碎片和 use-after-free)
- ASAN/MSAN 集成测试
- Windows Driver Verifier 检查(内核态)
4. 形式化验证
- 部分关键路径使用 Dafny 验证
- 状态机转换有数学证明
5. 代码审查
- 所有变更需要至少 2 人审查
- 安全相关变更需要安全团队审查
msquic 的代码结构
msquic/
├── src/
│ ├── core/ ← 协议核心
│ │ ├── connection.c ← 连接管理
│ │ ├── stream.c ← 流管理
│ │ ├── loss_detection.c ← 丢包检测
│ │ ├── congestion_control.c ← 拥塞控制
│ │ ├── crypto.c ← 加密帧处理
│ │ ├── packet.c ← 包构造/解析
│ │ └── ...
│ │
│ ├── platform/ ← 平台抽象层 (PAL)
│ │ ├── platform_winuser.c ← Windows 用户态
│ │ ├── platform_winkernel.c ← Windows 内核态
│ │ ├── platform_linux.c ← Linux
│ │ ├── datapath_winuser.c ← Windows UDP
│ │ ├── datapath_linux.c ← Linux UDP (epoll)
│ │ └── ...
│ │
│ ├── tls/ ← TLS 抽象
│ │ ├── tls_schannel.c ← Windows Schannel
│ │ ├── tls_openssl.c ← OpenSSL
│ │ └── ...
│ │
│ ├── bin/ ← 可执行文件
│ │ ├── quicreach/ ← 连接测试工具
│ │ └── ...
│ │
│ └── generated/ ← 自动生成的代码
│
├── inc/ ← 公共头文件
│ ├── msquic.h ← 主 API 头文件
│ └── msquic_posix.h ← POSIX 平台定义
│
├── submodules/ ← 依赖
│ └── openssl/
│
└── scripts/ ← 构建和测试脚本
msquic 的性能优化
关键优化技术:
1. RSS(Receive Side Scaling)
- 多核 CPU 并行处理收包
- 每个核一个 I/O 队列
- 避免核间通信开销
2. 批量发送
- Windows: WSASendMsg 批量
- Linux: sendmmsg 一次发多个包
- 减少系统调用次数
3. io_uring 支持(Linux 5.1+)
- 异步 I/O,避免 epoll 的 syscall 开销
- 批量提交和完成
- 零拷贝发送
4. 连接 ID 路由
- 服务端可以基于 Connection ID 路由到特定核
- 避免跨核锁争用
- 支持多核扩展
5. 内存池
- 预分配常用大小的内存块
- 避免运行时 malloc/free
- 减少内存碎片
性能数据(微软内部测试):
- 单核吞吐:~5 Gbps
- 多核吞吐:~100 Gbps (128 核)
- 连接建立:~100K connections/sec/core
- 内存占用:~8 KB/idle connection
ngtcp2:万能适配器
基本信息
项目:ngtcp2
GitHub: ngtcp2/ngtcp2
Stars: ~900
语言:C
定位:可嵌入的 QUIC 库 + TLS 库中立
架构:回调式 sans-io
代码量:~100 文件(核心)
TLS 后端:可选 OpenSSL / GnuTLS / wolfSSL / BoringSSL / Picotls
维护者:Tatsuhiro Tsujikawa(一人维护)
特色:nghttp2 的成功设计经验的延续
ngtcp2 的背景:nghttp2 的成功
ngtcp2 的作者 Tatsuhiro Tsujikawa 此前创建了 nghttp2——目前最广泛使用的 HTTP/2 C 库:
nghttp2 的成功:
- curl 的默认 HTTP/2 后端
- Apache httpd 的 HTTP/2 模块 (mod_http2)
- Python 的 hyper/h2 参考了它的设计
- 几乎所有 Linux 发行版都内置
ngtcp2 继承的设计原则:
1. TLS 库中立(不绑定任何一个 TLS 库)
2. C 语言(最大可移植性)
3. 回调式 API(灵活,嵌入友好)
4. 一个人维护但质量极高
5. 优秀的文档和示例
ngtcp2 的 TLS 中立设计
ngtcp2 最大的特色:它不绑定任何 TLS 库。
为什么这很重要?
不同环境有不同的 TLS 需求:
- 企业环境:必须用 OpenSSL(合规要求)
- 嵌入式:必须用 wolfSSL(体积小)
- Apple 平台:可能想用 BoringSSL
- GPL 项目:可能需要 GnuTLS(许可证兼容)
- 前沿研究:可能用 Picotls(Fastly 的实验 TLS)
ngtcp2 的解决方案:TLS 回调接口
┌─────────────────────────────────────────────────┐
│ ngtcp2 核心 │
│ ┌────────────────────────────────────────────┐ │
│ │ TLS 回调接口 │ │
│ │ - client_initial() │ │
│ │ - recv_crypto_data() │ │
│ │ - encrypt() │ │
│ │ - decrypt() │ │
│ │ - hp_mask() (Header Protection) │ │
│ └─────────────────┬──────────────────────────┘ │
└─────────────────────┼──────────────────────────────┘
│ 由适配层实现
┌───────────────┼───────────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────────▼─────────┐
│ OpenSSL │ │ GnuTLS │ │ wolfSSL/BoringSSL │
│ adapter │ │ adapter │ │ adapter │
└───────────┘ └───────────┘ └─────────────────────┘
ngtcp2 的回调式 API
ngtcp2 使用回调来通知应用层各种事件:
// ngtcp2 的使用方式
// 1. 设置回调函数表
ngtcp2_callbacks callbacks = {
.client_initial = client_initial_cb,
.recv_crypto_data = recv_crypto_data_cb,
.encrypt = encrypt_cb,
.decrypt = decrypt_cb,
.hp_mask = hp_mask_cb,
.recv_stream_data = recv_stream_data_cb,
.acked_stream_data_offset = acked_stream_data_offset_cb,
.stream_open = stream_open_cb,
.stream_close = stream_close_cb,
.rand = rand_cb,
.get_new_connection_id = get_new_connection_id_cb,
// ... 更多回调
};
// 2. 创建连接
ngtcp2_conn *conn;
ngtcp2_conn_client_new(&conn, &dcid, &scid, &path, version,
&callbacks, &settings, ¶ms,
NULL, user_data);
// 3. I/O 循环
for (;;) {
// 读取 UDP 数据
nread = recvfrom(fd, buf, sizeof(buf), 0, ...);
// 喂给 ngtcp2(类似 sans-io 的 input 端)
ngtcp2_conn_read_pkt(conn, &path, &pi, buf, nread, timestamp);
// 从 ngtcp2 获取要发送的数据
nwrite = ngtcp2_conn_write_pkt(conn, &path, &pi,
buf, sizeof(buf), timestamp);
if (nwrite > 0) {
sendto(fd, buf, nwrite, 0, ...);
}
// 处理超时
expiry = ngtcp2_conn_get_expiry(conn);
// 设置定时器...
}
注意:ngtcp2 的 API 风格是”回调 + 轮询”混合。核心逻辑不做 I/O(类似 sans-io),但通过回调把部分控制交给应用层。
ngtcp2 的流数据处理
// 发送流数据
ngtcp2_ssize nwrite;
ngtcp2_vec datav = {data, datalen};
// write_stream 同时做"写流数据"和"生成 QUIC 包"
nwrite = ngtcp2_conn_writev_stream(
conn, &path, &pi,
buf, sizeof(buf), // 输出缓冲区
&ndatalen, // 实际写入的流数据长度
NGTCP2_WRITE_STREAM_FLAG_NONE,
stream_id, // 目标流 ID
&datav, 1, // 要写的数据
timestamp
);
// nwrite = QUIC 包的总长度(包含头部、加密等)
// ndatalen = 实际被打包进去的流数据长度
// 接收流数据(通过回调)
int recv_stream_data_cb(
ngtcp2_conn *conn,
uint32_t flags,
int64_t stream_id,
uint64_t offset,
const uint8_t *data,
size_t datalen,
void *user_data,
void *stream_user_data
) {
// 应用层处理收到的流数据
printf("Stream %ld received %zu bytes\n", stream_id, datalen);
// 返回 0 表示成功
return 0;
}
ngtcp2 与 nghttp3:分层组合
ngtcp2 只做 QUIC 层,HTTP/3 由独立的 nghttp3 库实现:
┌─────────────────────┐
│ 应用层 │
├─────────────────────┤
│ nghttp3 │ ← HTTP/3 帧处理
│ (独立库) │
├─────────────────────┤
│ ngtcp2 │ ← QUIC 协议
│ (核心库) │
├─────────────────────┤
│ TLS adapter │ ← 可选 TLS 后端
│ (OpenSSL/...) │
├─────────────────────┤
│ UDP socket │ ← 应用负责
└─────────────────────┘
优点:
- 各层独立升级
- 不需要 HTTP/3 时不链接 nghttp3
- nghttp3 也可以用在其他 QUIC 库之上
这和 quinn 的理念类似:
quinn 没有内置 HTTP/3 → 用第三方 h3 crate
ngtcp2 没有内置 HTTP/3 → 用 nghttp3
ngtcp2 在 curl 中的应用
curl 是最广泛使用的 HTTP 客户端工具,它使用 ngtcp2 作为 QUIC 后端之一:
curl 的 HTTP/3 后端选择:
curl --with-ngtcp2 → ngtcp2 + nghttp3
curl --with-quiche → quiche (内含 HTTP/3)
curl --with-msh3 → msquic + msh3
为什么 curl 选择 ngtcp2 作为首选:
1. TLS 中立:curl 支持 12+ 种 TLS 库,ngtcp2 是唯一能适配它们的
2. C 语言:curl 本身是 C,零语言隔阂
3. 设计理念一致:都是"最小依赖、最大兼容"
4. nghttp2 的成功先例:curl 与 nghttp2 已合作多年
使用关系:
curl → libcurl → nghttp3 → ngtcp2 → OpenSSL/GnuTLS/wolfSSL
ngtcp2 的代码结构
ngtcp2/
├── lib/ ← 核心库
│ ├── ngtcp2_conn.c ← 连接主逻辑(最大文件)
│ ├── ngtcp2_conn.h
│ ├── ngtcp2_pkt.c ← 包解析/构造
│ ├── ngtcp2_crypto.c ← 加密抽象
│ ├── ngtcp2_strm.c ← 流管理
│ ├── ngtcp2_rtb.c ← 重传缓冲区
│ ├── ngtcp2_cc.c ← 拥塞控制
│ ├── ngtcp2_bbr.c ← BBR 实现
│ ├── ngtcp2_bbr2.c ← BBR2 实现
│ └── ...
│
├── crypto/ ← TLS 适配层
│ ├── openssl/ ← OpenSSL 适配
│ ├── gnutls/ ← GnuTLS 适配
│ ├── wolfssl/ ← wolfSSL 适配
│ ├── boringssl/ ← BoringSSL 适配
│ └── shared.c ← 共享加密工具
│
├── examples/ ← 示例程序
│ ├── client.cc ← C++ 示例客户端
│ ├── server.cc ← C++ 示例服务端
│ └── ...
│
├── tests/ ← 测试
│ └── ngtcp2_conn_test.c
│
└── doc/ ← API 文档
文件数量比 msquic 少很多,但每个文件都很”密实”——ngtcp2_conn.c 单文件就有数千行,包含了大部分连接逻辑。
msquic vs ngtcp2:详细对比
设计哲学对比
| 维度 | msquic | ngtcp2 |
|---|---|---|
| 开发团队 | 微软团队(10+ 人) | 一人维护 |
| 代码量 | ~700 文件 | ~100 文件 |
| I/O 模型 | 全包(内部管理) | 回调式 sans-io |
| TLS 后端 | Schannel / OpenSSL | 5 种可选 |
| HTTP/3 | 无(用 msh3) | 无(用 nghttp3) |
| 平台 | Windows + Linux | POSIX + Windows |
| 内核态 | 支持 | 不支持 |
| 多语言 | C API + .NET / Rust 绑定 | 纯 C API |
| 安全审计 | 极严格(fuzzing + SAL) | 作者自审 + 社区 |
API 风格对比
// msquic: 对象模型 + 事件回调
// 特点:申请对象 → 设置回调 → 启动 → 在回调中处理事件
MsQuic->ConnectionOpen(Registration, ConnectionCallback, NULL, &Connection);
MsQuic->ConnectionStart(Connection, Configuration, AF_UNSPEC, "example.com", 443);
// 之后一切发生在 ConnectionCallback 里
// ngtcp2: 轮询 + 回调混合
// 特点:应用主循环调用 read_pkt/write_pkt,部分事件通过回调通知
ngtcp2_conn_read_pkt(conn, &path, &pi, buf, nread, ts);
nwrite = ngtcp2_conn_write_pkt(conn, &path, &pi, out, outlen, ts);
// 流数据等通过回调获取
线程模型对比
msquic:
- 内部维护线程池
- 每个 CPU 核一个 worker 线程
- 连接固定绑定到特定核
- 回调在 worker 线程中执行
- 应用几乎不需要考虑线程问题
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Worker 0│ │ Worker 1│ │ Worker N│
│ CPU 0 │ │ CPU 1 │ │ CPU N │
│ Conn A │ │ Conn B │ │ Conn Z │
│ Conn C │ │ Conn D │ │ │
└─────────┘ └─────────┘ └─────────┘
ngtcp2:
- 不管理线程
- 应用自己决定线程模型
- 可以单线程(简单场景)
- 可以每连接一线程
- 可以用线程池 + epoll
┌─────────────────────────────────┐
│ 应用的线程模型 │
│ (ngtcp2 不关心你怎么组织) │
│ │
│ 可以是 event loop │
│ 可以是 thread-per-connection │
│ 可以是 async runtime │
└─────────────────────────────────┘
拥塞控制对比
msquic 的拥塞控制:
- CUBIC(默认)
- BBR(实验性)
- 自定义接口(可插入新算法)
特点:
- 针对 Windows 网络栈优化
- 与 RSS 联动(每核独立 CC 状态)
- 支持 HyStart++
ngtcp2 的拥塞控制:
- NewReno
- CUBIC
- BBR
- BBR2
特点:
- 实现严格遵循 RFC/论文
- 代码清晰,适合参考
- BBR2 是少数实现了最新版本的库之一
连接状态机对比
msquic 的连接状态
msquic 连接状态机(简化):
┌───────────┐
│ Allocated │ ← ConnectionOpen() 创建
└─────┬─────┘
│ ConnectionStart() / 收到 Initial
▼
┌───────────┐
│ Handshake │ ← TLS 握手进行中
│ (client/ │
│ server) │
└─────┬─────┘
│ 握手完成
▼
┌───────────┐
│ Connected │ ← 正常数据传输
│ │ 可以开流、发数据
└─────┬─────┘
│ 超时 / 收到 CONNECTION_CLOSE / 应用关闭
▼
┌───────────┐
│ Shutdown │ ← 发送 CONNECTION_CLOSE
│ Pending │ 等待 ACK
└─────┬─────┘
│ 确认或超时
▼
┌───────────┐
│ Closed │ ← 资源释放
└───────────┘
msquic 通过回调通知状态变化:
QUIC_CONNECTION_EVENT_CONNECTED → 握手完成
QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT → 传输层关闭
QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER → 对端关闭
QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE → 完全关闭
ngtcp2 的连接状态
ngtcp2 连接状态(通过 API 查询):
ngtcp2_conn_get_handshake_completed(conn)
→ 0 = 握手中, 1 = 握手完成
ngtcp2_conn_in_closing_period(conn)
→ 正在关闭(发送了 CONNECTION_CLOSE,等待排空)
ngtcp2_conn_in_draining_period(conn)
→ 排空期(收到了对端的 CONNECTION_CLOSE,静默等待)
ngtcp2 不用 enum 表示状态,而是用一系列布尔查询函数
→ 更灵活但不如 enum 直观
→ 应用需要自己追踪完整状态
在 TQUIC 上下文中的意义
理解 msquic 和 ngtcp2 对学习 TQUIC 有什么帮助?
msquic 对 TQUIC 的参考价值:
1. 内部线程池管理 → TQUIC 也有类似设计
2. 平台抽象层 → TQUIC 也需要跨平台
3. 性能优化技术(RSS, io_uring)→ TQUIC 可借鉴
4. 内核态可能性 → 学术讨论的方向
ngtcp2 对 TQUIC 的参考价值:
1. TLS 中立设计 → TQUIC 的 TLS trait 抽象
2. 拥塞控制算法实现 → BBR2 的参考实现
3. 一人维护的代码质量 → 代码简洁性的标杆
4. 与 curl 的集成方式 → 如何设计嵌入式 API
TQUIC 的定位对比:
msquic → 微软生态的 QUIC
ngtcp2 → 万能适配的 QUIC
TQUIC → 多路径 + 高性能的 QUIC(腾讯)
TQUIC 独特之处:
- 多路径是一等公民(msquic/ngtcp2 都不支持)
- Rust 实现但有 C/FFI + 移动端支持
- 拥塞控制可插拔且支持 per-path
构建和体验
构建 msquic
# 克隆(包含子模块)
git clone --recursive https://github.com/microsoft/msquic.git
cd msquic
# Linux 构建
mkdir build && cd build
cmake -G 'Unix Makefiles' ..
cmake --build .
# 运行示例
./bin/Release/quicsample -server -cert_file:cert.pem -key_file:key.pem
./bin/Release/quicsample -client -target:localhost
# 注意:msquic 的构建比较重
# 需要 CMake 3.16+, Perl (for OpenSSL), 以及较新的 GCC/Clang
构建 ngtcp2
# 克隆
git clone https://github.com/ngtcp2/ngtcp2.git
cd ngtcp2
git submodule update --init
# 使用 OpenSSL 构建
autoreconf -i
./configure --with-openssl
make
make check # 运行测试
# 运行示例
./examples/server 0.0.0.0 4433 cert.pem key.pem
./examples/client 127.0.0.1 4433
# ngtcp2 的构建相对简单
# 主要依赖:autotools, 选择的 TLS 库
对比总结表
| 维度 | msquic | ngtcp2 |
|---|---|---|
| 一句话定位 | 微软全平台 QUIC 引擎 | 万能 TLS 适配的轻量 QUIC |
| 代码量 | 大(700+ 文件) | 小(100 文件) |
| 开发团队 | 微软团队 | 一人(Tatsuhiro Tsujikawa) |
| I/O 模型 | 内部全管理 | 应用自行管理 |
| TLS | Schannel + OpenSSL | 5 种可选 |
| 内核态 | 支持 | 不支持 |
| 拥塞控制 | CUBIC, BBR | NewReno, CUBIC, BBR, BBR2 |
| 学习价值 | 性能优化、平台抽象 | API 设计、拥塞控制实现 |
| 适合场景 | Windows 生态、超大规模 | curl 等工具、多 TLS 环境 |
| 对 TQUIC | 性能优化参考 | BBR2 实现参考、API 设计参考 |
读完本章你能做什么
- 解释 msquic 为什么不用 sans-io 模式(内核态需求 + 全管理哲学)
- 描述 ngtcp2 的 TLS 中立设计如何工作(回调接口 + 适配层)
- 比较”对象模型 + 回调”(msquic)和”轮询 + 回调”(ngtcp2)两种 API 风格
- 说出 msquic 内核态运行的性能优势(零拷贝、零系统调用开销)
- 解释 ngtcp2 在 curl 中的角色和为什么被选为首选后端
- 将 msquic 和 ngtcp2 的设计特点与 TQUIC 关联
常见误区
误区一:msquic 只能在 Windows 上运行
正确理解:msquic 从一开始就是跨平台设计。它在 Linux 上有完整的支持(使用 OpenSSL 作为 TLS 后端,使用 epoll/io_uring 做 I/O)。Azure 的很多 Linux 服务都在用 msquic。”Windows 内核态”只是它的额外能力,不是唯一运行方式。
误区二:ngtcp2 因为一个人维护所以不可靠
正确理解:Tatsuhiro Tsujikawa 也是 nghttp2 的作者——那个库被 curl、Apache、几乎所有 Linux 发行版使用。一人维护不等于低质量——反而因为风格统一、设计一致,代码质量极高。curl 的创建者 Daniel Stenberg 多次公开赞扬 ngtcp2 的代码质量。但确实有”bus factor”风险(如果作者不能继续维护怎么办)。
误区三:C 语言写网络库不安全
正确理解:C 语言确实没有 Rust 的内存安全保证,但 msquic 通过 SAL 注解 + 持续 fuzzing + ASAN 来弥补。ngtcp2 通过精心的编码规范和测试来保证。关键不是语言本身,而是开发实践。用 C 的好处是:最大的跨平台兼容性、最小的运行时开销、最广的语言互操作性。这就是为什么 TQUIC 选择了 Rust(安全性)但提供 C FFI(兼容性)——两者兼顾。
导读目录:README.md 上一章:第 7 章 · 社区标杆:quinn 与 quiche——极简 vs 全能 下一章:第 9 章 · 安全与性能:s2n-quic——AWS 的 Rust 工程标杆