ProtoStack 是一个从 UDP 套接字到 HTTP/3 的完整 QUIC 协议实现,包含 0-RTT 握手、连接迁移、QPACK 头部压缩与 BBRv3 拥塞控制算法。项目深入网络协议栈底层,展示了高性能网络编程的深厚功底。
一、QUIC 协议架构
QUIC 基于 UDP 实现了类 TCP 的可靠传输,同时解决了 TCP 的队头阻塞问题。每个 Stream 独立传输,一个 Stream 的丢包不影响其他 Stream。QUIC 将 TLS 1.3 集成到握手过程中,实现 0-RTT 和 1-RTT 握手。
use std::net::UdpSocket;
use ring::aead;
pub struct QuicConnection {
socket: UdpSocket,
connection_id: ConnectionId,
state: ConnectionState,
streams: HashMap,
crypto: QuicCrypto,
congestion: Box,
loss_detection: LossDetection,
}
pub struct QuicStream {
id: StreamId,
send_buffer: VecDeque,
recv_buffer: BTreeMap>,
send_offset: u64,
recv_offset: u64,
state: StreamState,
flow_control: FlowController,
}
二、0-RTT 握手实现
QUIC 的 0-RTT 允许客户端在首次握手时就发送应用数据,前提是客户端已缓存服务器的静态配置。这需要 TLS 1.3 的 early_data 扩展和 QUIC 的地址验证令牌机制。
impl QuicConnection {
pub fn connect_0rtt(&mut self, server_addr: SocketAddr,
early_data: &[u8]) -> Result<()>
{
// 1. 使用缓存的服务器配置构建 ClientHello
let cached_config = self.load_cached_config(server_addr)?;
let client_hello = self.build_client_hello_0rtt(&cached_config);
// 2. 发送 Initial 包 + 0-RTT 数据
let initial_packet = self.build_initial_packet(&client_hello);
let zero_rtt_packet = self.build_0rtt_packet(early_data,
&cached_config.early_data_key);
self.socket.send_to(&initial_packet, server_addr)?;
self.socket.send_to(&zero_rtt_packet, server_addr)?;
// 3. 等待服务器确认
self.state = ConnectionState::WaitingForHandshake;
Ok(())
}
fn build_0rtt_packet(&self, data: &[u8],
key: &aead::LessSafeKey) -> Vec
{
let mut packet = QuicPacket::new(PacketType::ZeroRTT);
let frame = CryptoFrame { offset: 0, data: data.to_vec() };
packet.add_frame(QuicFrame::Crypto(frame));
// 使用 early_data 密钥加密
let nonce = self.generate_nonce();
let encrypted = key.seal_in_place_append_tag(nonce,
aead::Aad::empty(), &mut packet.payload)?;
packet.build()
}
}
三、连接迁移
QUIC 的连接由 Connection ID 标识,而非 IP+Port 四元组。这使得客户端在切换网络(WiFi→5G)时无需重新握手,只需用新地址发送携带相同 Connection ID 的数据包即可。
impl QuicConnection {
pub fn migrate_connection(&mut self, new_addr: SocketAddr) -> Result<()> {
// 1. 发送 PATH_CHALLENGE 帧验证新路径可达
let challenge_data = rand::random::<[u8; 8]>();
let frame = QuicFrame::PathChallenge {
data: challenge_data,
};
self.send_frame(frame, new_addr)?;
// 2. 等待 PATH_RESPONSE
let response = self.wait_for_path_response(challenge_data,
Duration::from_secs(3))?;
// 3. 验证通过,切换路径
if response.data == challenge_data {
self.current_path = new_addr;
self.path_validated = true;
// 4. 在新路径上探测带宽
self.congestion.on_path_change();
self.probe_bandwidth(new_addr)?;
}
Ok(())
}
}
四、BBRv3 拥塞控制
BBRv3 是 Google 开发的最新一代拥塞控制算法,通过持续探测带宽和 RTT 来找到最优发送速率,而非依赖丢包信号。相比 CUBIC,BBRv3 在高延迟、有损链路上吞吐量提升 10-100 倍。
pub struct BBRv3 {
state: BBRState,
max_bw: f64, // 最大带宽估计
min_rtt: Duration, // 最小 RTT 估计
pacing_rate: f64, // 当前发送速率
cwnd: u64, // 拥塞窗口
cycle: BBRCycle,
}
#[derive(PartialEq)]
enum BBRState {
Startup, // 指数增长探测带宽
Drain, // 排空队列
ProbeBW, // 周期性探测更多带宽
ProbeRTT, // 周期性探测最小 RTT
}
impl BBRv3 {
pub fn on_ack(&mut self, packet: &AckedPacket) {
let delivery_rate = packet.delivered / packet.interval.as_secs_f64();
self.max_bw = self.max_bw.max(delivery_rate);
self.min_rtt = self.min_rtt.min(packet.rtt);
match self.state {
BBRState::Startup => {
self.pacing_rate = self.max_bw * 2.89; // 2/ln(2)
if self.pacing_rate_gain() < 1.0 {
self.state = BBRState::Drain;
}
}
BBRState::ProbeBW => {
self.cycle.advance();
self.pacing_rate = self.max_bw * self.cycle.gain();
}
_ => {}
}
self.cwnd = (self.pacing_rate * self.min_rtt.as_secs_f64()
* 2.0) as u64;
}
}
五、QPACK 头部压缩
QPACK 是 HTTP/3 的头部压缩方案,基于 QCRAM 设计,解决了 HPACK 在 QUIC 中的队头阻塞问题。使用两个单向流分别传输编码器和解码器的表更新。
ProtoStack 是网络协议栈领域的深度实践,从传输层到应用层完整实现,展示了高性能网络编程的核心能力。
← 返回作品展示