3 个版本 (破坏性)
0.3.0 | 2024 年 1 月 24 日 |
---|---|
0.2.0 | 2022 年 5 月 24 日 |
0.1.0 | 2022 年 4 月 8 日 |
#740 在 网络编程
每月 25,205 次下载
在 38 个组件中使用 (5 个直接使用)
38KB
879 行
quiche 是 IETF 规定的 QUIC 传输协议和 HTTP/3 的实现。它提供了处理 QUIC 数据包和处理连接状态的低级 API。应用程序负责提供 I/O(例如套接字处理)以及支持定时器的事件循环。
有关 quiche 的产生及其设计的更多详细信息,您可以阅读 Cloudflare 博客上的文章。
谁在使用 quiche?
Cloudflare
quiche 为 Cloudflare 边缘网络的 HTTP/3 支持 提供动力。可以使用 cloudflare-quic.com 网站进行测试和实验。
Android
Android 的 DNS 解析器使用 quiche 来 实现 DNS over HTTP/3。
curl
可以将 quiche 集成到 curl 中以提供对 HTTP/3 的支持。查看集成方法。
NGINX(非官方)
可以使用非官方补丁将 quiche 集成到 NGINX 中以提供对 HTTP/3 的支持。查看集成方法。
入门
命令行应用程序
在深入了解 quiche API 之前,这里有一些使用作为 quiche-apps 组件的一部分提供的 quiche 工具的示例。
根据 构建 部分中提到的命令克隆项目后,客户端可以按照以下方式运行:
$ cargo run --bin quiche-client -- https://cloudflare-quic.com/
而服务器可以按照以下方式运行:
$ cargo run --bin quiche-server -- --cert apps/src/bin/cert.crt --key apps/src/bin/cert.key
(请注意,提供的证书是自签名的,不应在生产中使用)
使用 --help
命令行标志可以获得每个工具选项的更详细描述。
配置连接
使用quiche建立QUIC连接的第一步是创建一个Config
对象。
let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
config.set_application_protos(&[b"example-proto"]);
// Additional configuration specific to application and use case...
Config
对象控制QUIC连接的重要方面,如QUIC版本、ALPN标识、流量控制、拥塞控制、空闲超时以及其他属性或功能。
QUIC是一种通用传输协议,有几个配置属性没有合理的默认值。例如,任何特定类型的并发流数取决于在QUIC上运行的应用程序以及其他特定用例的考虑。
quiche将多个属性默认设置为零,应用程序很可能需要根据以下内容将其设置为其他值以满足其需求:
set_initial_max_streams_bidi()
set_initial_max_streams_uni()
set_initial_max_data()
set_initial_max_stream_data_bidi_local()
set_initial_max_stream_data_bidi_remote()
set_initial_max_stream_data_uni()
Config
还包含TLS配置。这可以通过对现有对象的突变者进行更改,或者通过手动构建TLS上下文并使用with_boring_ssl_ctx_builder()
创建配置来更改。
配置对象可以在多个连接之间共享。
连接设置
在客户端,可以使用connect()
实用函数来创建新的连接,而accept()
则用于服务器。
// Client connection.
let conn = quiche::connect(Some(&server_name), &scid, local, peer, &mut config)?;
// Server connection.
let conn = quiche::accept(&scid, None, local, peer, &mut config)?;
处理传入的数据包
应用程序可以使用连接的recv()
方法处理属于该连接的来自网络的传入数据包。
let to = socket.local_addr().unwrap();
loop {
let (read, from) = socket.recv_from(&mut buf).unwrap();
let recv_info = quiche::RecvInfo { from, to };
let read = match conn.recv(&mut buf[..read], recv_info) {
Ok(v) => v,
Err(e) => {
// An error occurred, handle it.
break;
},
};
}
生成传出数据包
可以使用连接的send()
方法生成传出数据包。
loop {
let (write, send_info) = match conn.send(&mut out) {
Ok(v) => v,
Err(quiche::Error::Done) => {
// Done writing.
break;
},
Err(e) => {
// An error occurred, handle it.
break;
},
};
socket.send_to(&out[..write], &send_info.to).unwrap();
}
发送数据包时,应用程序负责维护计时器以响应基于时间的连接事件。可以使用连接的timeout()
方法获取计时器到期时间。
let timeout = conn.timeout();
应用程序负责提供计时器实现,这可能特定于操作系统或使用的网络框架。当计时器到期时,应调用连接的on_timeout()
方法,之后可能需要在网络上发送更多数据包。
// Timeout expired, handle it.
conn.on_timeout();
// Send more packets as needed after timeout.
loop {
let (write, send_info) = match conn.send(&mut out) {
Ok(v) => v,
Err(quiche::Error::Done) => {
// Done writing.
break;
},
Err(e) => {
// An error occurred, handle it.
break;
},
};
socket.send_to(&out[..write], &send_info.to).unwrap();
}
节流
建议应用程序节流发送传出数据包,以避免创建可能导致网络短期拥塞和丢失的数据包突发。
quiche通过SendInfo
结构体的[at
]字段公开传出数据包的节流提示,该结构体由send()
方法返回。此字段表示特定数据包应发送到网络中的时间。
应用程序可以通过平台特定的机制(例如Linux上的SO_TXTIME
套接字选项)或自定义方法(例如使用用户空间定时器)人为地延迟发送数据包来使用这些提示。
发送和接收流数据
经过一番来回后,连接将完成握手并准备好发送或接收应用程序数据。
可以通过使用stream_send()
方法在流上发送数据
if conn.is_established() {
// Handshake completed, send some data on stream 0.
conn.stream_send(0, b"hello", true)?;
}
应用程序可以使用连接的readable()
方法检查是否有任何可读的流,该方法返回一个遍历所有待读取数据的流的迭代器。
然后可以使用stream_recv()
方法从可读流中检索应用程序数据
if conn.is_established() {
// Iterate over readable streams.
for stream_id in conn.readable() {
// Stream is readable, read until there's no more data.
while let Ok((read, fin)) = conn.stream_recv(stream_id, &mut buf) {
println!("Got {} bytes on stream {}", read, stream_id);
}
}
}
HTTP/3
quiche HTTP/3模块为在QUIC传输协议之上发送和接收HTTP请求和响应提供了一个高级API。
请参阅quiche/examples/目录,了解更多如何使用quiche API的完整示例,包括如何在C/C++应用程序中使用quiche的示例(以下提供更多信息)。
从C/C++调用quiche
quiche在Rust API之上公开了一个精简的C API,可以更轻松地将quiche集成到C/C++应用程序中(以及允许通过某些形式的FFI调用C API的其他语言)。C API遵循与Rust相同的结构,但受到C语言本身的约束。
在运行cargo build
时,将与Rust版本一起自动构建一个名为libquiche.a
的静态库。这是一个完全独立的库,可以直接链接到C/C++应用程序中。
请注意,为了启用FFI API,必须启用ffi
功能(默认情况下是禁用的),通过向cargo
传递--features ffi
来实现。
构建
quiche需要Rust 1.66或更高版本来构建。可以使用rustup安装最新的稳定版Rust。
设置好Rust构建环境后,可以使用git获取quiche源代码
$ git clone --recursive https://github.com/cloudflare/quiche
然后使用cargo进行构建
$ cargo build --examples
cargo还可以用于运行测试套件
$ cargo test
请注意,用于实现基于TLS的QUIC的加密握手的BoringSSL需要构建并链接到quiche。当使用cargo构建quiche时,这会自动完成,但需要在构建过程中提供cmake
命令。在Windows上,还需要NASM。有关详细信息,请参阅官方BoringSSL文档。
作为替代,您可以使用自己的自定义构建的BoringSSL,通过使用QUICHE_BSSL_PATH
环境变量配置BoringSSL目录来实现。
$ QUICHE_BSSL_PATH="/path/to/boringssl" cargo build --examples
或者您可以使用 OpenSSL/quictls。要启用 quiche 使用此供应商,可以将 openssl
功能添加到 --feature
列表中。请注意,如果使用此供应商,则不支持 0-RTT
。
为 Android 构建
使用 NDK 版本 19 或更高版本(建议使用 21),可以使用 cargo-ndk(v2.0 或更高版本)为 Android 构建 quiche。
首先需要安装 Android NDK,可以使用 Android Studio 或直接安装,并将环境变量 ANDROID_NDK_HOME
设置为 NDK 安装路径,例如:
$ export ANDROID_NDK_HOME=/usr/local/share/android-ndk
然后,可以按照以下方式安装所需的 Android 架构的 Rust 工具链:
$ rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
请注意,所有目标架构的最小 API 级别为 21。
还需要安装 cargo-ndk(v2.0 或更高版本)
$ cargo install cargo-ndk
最后,可以使用以下步骤构建 quiche 库。请注意,-t <架构>
和 -p <NDK 版本>
选项是必需的。
$ cargo ndk -t arm64-v8a -p 21 -- build --features ffi
有关更多信息,请参阅 build_android_ndk19.sh。
为 iOS 构建
要为 iOS 构建 quiche,您需要以下内容:
- 安装 Xcode 命令行工具。您可以使用 Xcode 安装它们,或者使用以下命令安装:
$ xcode-select --install
- 安装 iOS 架构的 Rust 工具链
$ rustup target add aarch64-apple-ios x86_64-apple-ios
- 安装
cargo-lipo
$ cargo install cargo-lipo
要构建 libquiche,请运行以下命令:
$ cargo lipo --features ffi
或
$ cargo lipo --features ffi --release
iOS 构建已在 Xcode 10.1 和 Xcode 11.2 中测试。
构建 Docker 镜像
为了构建 Docker 镜像,只需运行以下命令:
$ make docker-build
您可以在以下 Docker Hub 仓库找到 quiche Docker 镜像:
每当 quiche master 分支更新时,将更新 latest
标签。
cloudflare/quiche
提供安装在 /usr/local/bin 中的服务器和客户端。
cloudflare/quiche-qns
提供在 quic-interop-runner 中测试 quiche 的脚本。
版权
版权(C)2018-2019,Cloudflare,Inc。
有关许可信息,请参阅 COPYING。