32个版本 (21个重大变更)
0.22.0 | 2024年6月26日 |
---|---|
0.20.1 | 2024年3月12日 |
0.20.0 | 2023年12月12日 |
0.19.0 | 2023年11月10日 |
0.1.0-alpha3 | 2019年3月16日 |
#27 in 网络编程
27,967 每月下载量
用于 14 个包 (10个直接)
16MB
464K SLoC
quiche 是根据 IETF 指定的QUIC传输协议和HTTP/3的实现。它提供了一个低级API来处理QUIC数据包和处理连接状态。应用程序负责提供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 crate中提供的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 ID、流量控制、拥塞控制、空闲超时以及其他属性或功能。
QUIC是一种通用传输协议,有几个配置属性没有合理的默认值。例如,任何特定类型的并发流的允许数量取决于运行在QUIC上的应用程序以及其他特定用例的考虑。
quiche将几个属性默认设置为0,应用程序很可能需要使用以下方法将它们设置为其他值以满足其需求:
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配置。这可以通过现有对象的mutators进行更改,或者通过手动构建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
字段提供的出站数据包的速率提示。这个字段表示特定数据包应该发送到网络中的时间。
应用程序可以通过平台特定的机制(如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 API相同的设计,除了C语言本身的约束。
当运行cargo build
时,将自动构建一个名为libquiche.a
的静态库,与Rust库一起构建。这是一个完全独立的库,可以直接链接到C/C++应用程序中。
请注意,为了启用FFI API,必须启用ffi
功能(默认情况下是禁用的),通过将--features ffi
传递给cargo
来实现。
构建
quiche需要Rust 1.66或更高版本才能构建。可以使用rustup安装最新的稳定Rust版本。
设置好Rust构建环境后,可以使用git获取quiche源代码
$ git clone --recursive https://github.com/cloudflare/quiche
然后使用cargo进行构建
$ cargo build --examples
cargo还可以用来运行测试套件
$ cargo test
请注意,BoringSSL,它用于实现基于TLS的QUIC的加密握手,需要构建并将其链接到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构建
对于Android(NDK版本19或更高版本,建议21),可以使用cargo-ndk(v2.0或更高版本)构建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 <architecture>
和-p <NDK version>
选项。
$ 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。
依赖项
~6–19MB
~384K SLoC