TCP 拥塞控制与 QUIC 传输

共 64 题
📑 题目列表 64 题
#
★★★

1. TCP 序列号、累计确认与 SACK 如何协同恢复多个丢失报文段?

请说明 TCP 的序列号、累计确认与 SACK 如何协同,以恢复多个丢失的报文段?

  • 序列号与编号
  • 累计确认(cumulative ACK)与 SACK
  • 多段丢失的恢复

TCP 用序列号给每个字节编号,接收端按序接收。累计确认(cumulative ACK)表明"该序列号之前的所有字节都已收到",但面对多个丢失段,累计确认只反映连续接收的最高序列号,无法表达"中间空洞后还有哪些收到了",导致发送端只能重传最早丢失的段(Go-Back-N 式,效率低)。SACK(Selective ACK)在 ACK 中携带"已接收的不连续段"范围,让发送端明确知道哪些段已收到、哪些丢了,从而精确重传缺失的段,避免重复重传已收到的段。协同机制:序列号标识数据位置,累计确认提供连续进度,SACK 补充"空洞中的已收段",发送端据此重传所有缺失段,一次 RTT 内恢复多个丢失段。

累计确认是"连续进度",SACK 是"不连续详情",结合两者才能高效恢复多个丢失段。SACK 解决了多段丢失时"冗余重传"与"重传延迟"问题。

#
★★

2. ECN 与 DCTCP 在数据中心部署中如何降低丢包率,交换机标记位与 AQM 队列管理如何配合?

请说明 ECN 与 DCTCP 在数据中心如何降低丢包率,以及交换机标记位与 AQM 队列管理如何配合?

  • ECN(显式拥塞通知)
  • DCTCP 的阈值标记与窗口调整
  • AQM 队列管理与标记

ECN 通过 IP 头 CE 位(Congestion Experienced)和 TCP 头 ECE/CWR 位让网络"标记拥塞"而非丢弃,降低丢包率。DCTCP 在数据中心部署:交换机用 AQM(如 RED/WRED)在队列长度超过低阈值(如 K 个包)时把包标记 CE 位(而非丢弃),接收端回 ECE,发送端按"被标记比例"(基于窗口内标记包的占比)精确调整拥塞窗口,能快速响应并保持队列低位,从而显著降低丢包与延迟。交换机标记与 AQM 的配合:AQM 根据队列占用动态决定是否标记,DCTCP 用很小的固定阈值(K)触发标记,使队列在阈值附近震荡而非堆积,既避免丢包又保持低延迟。DCTCP 相比 TCP 的 ECN 更激进(阈值低、按比例调整),数据中心效果好。

ECN 用"标记"替代"丢弃",DCTCP 用"低阈值 AQM 标记 + 按标记比例调窗"实现低延迟低丢包。这是数据中心(低延迟)场景的关键优化。

#
★★

3. TCP 慢启动门限(ssthresh)与拥塞避免阶段的切换逻辑,packet pacing 与 GSO 对吞吐稳定性的影响是什么?

请说明 TCP 慢启动门限(ssthresh)与拥塞避免阶段的切换逻辑,以及 packet pacing 与 GSO 对吞吐稳定性的影响?

  • ssthresh 与慢启动/拥塞避免切换
  • packet pacing(均匀发送)
  • GSO 与吞吐稳定性

TCP 在慢启动阶段窗口指数增长(每 RTT 翻倍),当窗口达到 ssthresh 时切换为拥塞避免,窗口线性增长(每 RTT 加 1)。遇到丢包时 ssthresh 被下调(通常为当前窗口的一半),窗口重置,再进入慢启动。切换逻辑由 ssthresh 决定:窗口 < ssthresh 用慢启动,≥ ssthresh 用拥塞避免。packet pacing 是均匀地按速率发送包(而非突发),避免突发造成的队列堆积与丢包,使吞吐更稳定;GSO(Generic Segmentation Offload)把大报文交给网卡分段,减少内核处理次数,但若突发发送(burst),可能造成短时突发拥塞。抖动与稳定性上,pacing 平滑数据流、GSO 提升吞吐但需配合 pacing 避免突发。

ssthresh 是慢启动与拥塞避免的"分界",pacing 平滑发送、GSO 减开销。两者结合(pacing 控制节奏 + GSO 提升效率)可稳定高吞吐。

#
★★

4. TCP 的 TIME_WAIT 状态为何保留 2MSL,对短连接高并发服务(HTTP/1.0)的端口耗尽问题如何缓解?

请说明 TCP 的 TIME_WAIT 状态为何保留 2MSL,以及短连接高并发服务(HTTP/1.0)下如何缓解 TIME_WAIT 导致的端口耗尽?

  • TIME_WAIT 与 2MSL
  • 端口耗尽问题
  • 缓解(SO_REUSEADDR、短寿、连接复用)

TIME_WAIT 是主动关闭方在发送最后一个 ACK 后进入的状态,持续 2MSL(最大报文段寿命的 2 倍,约 60s)。原因:确保最后的 ACK 能到达对端(若 ACK 丢失,对端重发 FIN,本端可重发 ACK);使旧连接的延迟报文在网络中自然消亡,避免污染新连接。对短连接高并发(HTTP/1.0 每请求一条连接),服务端大量主动关闭产生大量 TIME_WAIT,占用端口(4 元组中的源端口)导致端口耗尽。缓解:启用 SO_REUSEADDR/SO_REUSEPORT 允许复用端口;设置 sysctl net.ipv4.tcp_tw_reuse(内核复用 TIME_WAIT 端口)、tcp_timestamps;使用连接复用(keep-alive/连接池)减少新建连接;缩短 TIME_WAIT 或调整端口范围。

2MSL 是"可靠收尾 + 防旧报文污染"的代价。端口耗尽源于大量 TIME_WAIT 占用 4 元组,缓解方向是复用端口、复用连接、缩短状态。

#
★★

5. UDP 应用若自行实现重试、分片与拥塞控制,必须处理哪些放大和公平性问题?

请说明 UDP 应用若自行实现重试、分片与拥塞控制,必须处理哪些放大效应和公平性问题?

  • UDP 无内建重传/拥塞控制
  • 放大攻击(amplification)
  • 公平性(与 TCP 竞争)

UDP 无内建可靠性与拥塞控制,应用自实现时须处理:1) 放大问题——重试应避免"请求放大响应"(如用大于请求的响应会放大攻击,需限制响应大小与验证来源,如 address validation);2) 分片与 MTU——自行处理分片/重组与 PMTUD,避免因入口过滤导致黑洞;3) 拥塞控制——若自实现拥塞控制,需像 TCP 一样有慢启动、退避、避免对网络造成拥塞崩溃,否则会与 TCP 流量竞争时"不公平"(不遵守拥塞窗口会挤占 TCP 的带宽);4) 公平性——在共享瓶颈上与 TCP 公平竞争,需实现类似 AIMD 的拥塞控制,否则会损害其他 TCP 流。本质上,UDP 应用若要具备可靠、公平、防放大,必须把这些机制自己实现。

UDP 是"裸传输",可靠性/拥塞/公平/防放大都要应用自建。放大与公平是自实现 UDP 协议最易踩的两大坑。

#
★★

6. MPTCP 在多路径调度上的 scheduler 与 congestion control 是如何配合的,子流独立拥塞与耦合拥塞的取舍是什么?

请说明 MPTCP 在多路径调度上 scheduler 与 congestion control 如何配合,以及子流独立拥塞与耦合拥塞的取舍?

  • MPTCP 的 scheduler(调度器)
  • 子流拥塞控制(独立 vs 耦合)
  • 吞吐与公平性取舍

MPTCP 把连接拆成多个子流(subflow),每个子流走一条路径。scheduler 决定把数据分发到哪个子流(如按 RTT 排序、优先最畅通路径、按窗口分配),congestion control 决定每个子流的发送速率。两者配合:scheduler 根据各子流的拥塞窗口/时延/RTT 把包分发,congestion control 根据各子流反馈调整窗口。子流拥塞控制有两种:独立拥塞(每个子流独立 AIMD,用 TCP 的 Reno/CUBIC,总吞吐=各子流之和,但可能对各路径不友好)与耦合拥塞(如 LIA/OLIA/耦合算法,把各子流联合起来,保证总吞吐不劣于单路径 TCP,且对各路径 TCP 流公平)。取舍:独立拥塞简单、吞吐高但可能不公平;耦合拥塞公平、鲁棒但需跨子流协调、实现复杂。

scheduler 管"分发",congestion control 管"速率",二者配合实现多路径吞吐。独立 vs 耦合拥塞是"吞吐"与"公平/鲁棒"的取舍。

#
★★

7. 为什么 L4 负载均衡器在 ACK/SYN 报文经过时必须保证会话保持,DSR(Direct Server Return)模式下 NAT 表项如何维护?

请说明 L4 负载均衡器在 ACK/SYN 报文经过时必须保证会话保持的原因,以及 DSR(直接服务器返回)模式下 NAT 表项如何维护?

  • L4 会话保持与状态
  • DSR 模式与 NAT 表维护
  • 转发路径

L4 负载均衡器是状态化的,它按 4 元组(或 5 元组)记录会话状态,决定把同一连接的所有报文(SYN、ACK、DATA)转发到同一后端。若 ACK/SYN 被负载到不同后端,连接无法建立或中断,因此必须保持会话(同一连接同一后端)。DSR(Direct Server Return)模式下,负载均衡器只处理入站(客户端→后端)方向,把报文转发给后端(可能改目标 IP 或仅选后端),后端直接回包给客户端(不回经负载均衡器),从而减轻负载器压力。DSR 下 NAT 表项维护:负载器通常不需要维护完整的回程 NAT 表(因为回包不走负载器),只需在入站方向做"选后端"(如 CIP 不改或改目标 MAC),回程由后端直接发,负载器无需对应 NAT 表项;若用传统 NAT(Full NAT),则需维护双向 NAT 表,但 DSR 常采用"只改目标 IP 或 MAC"以减少状态。

会话保持是状态化四层转发的核心,DSR 通过"回程直连"减少负载器状态与压力。DSR 下入站做选后端、出站直连,无需全量双向 NAT。

#
★★

8. QUIC 的拥塞控制(QUIC-CC)相比 TCP CC 在 ACK 路径上做了哪些改进,跨流协调与 pacing 差异如何体现?

请说明 QUIC 的拥塞控制(QUIC-CC)相比 TCP 拥塞控制(TCP-CC)在 ACK 路径上做了哪些改进,以及跨流协调与 pacing 差异如何体现?

  • QUIC 在 ACK 路径上的改进
  • 跨流协调(流级 vs 连接级)
  • pacing 差异

QUIC 的拥塞控制在用户态实现,可插拔,且更细粒度:ACK 路径上,QUIC 支持更精确的 ACK 信息(如 ACK 范围、延迟、ACK 频率控制),可精确区分丢包与乱序,支持更细的 RTT 采样与 pacing;QUIC 的 ACK 由传输层控制,可调整 ACK 频率(降低 ACK 开销)。跨流协调:QUIC 的拥塞控制是连接级的(所有 stream 共享一个拥塞窗口),但流级流控独立,配合连接级窗口避免整体过载;拥塞控制基于连接而非流,保证各流公平共享。pacing 差异:QUIC 拥塞控制普遍内置 pacing(按速率均匀发送),避免突发,比传统 TCP 的突发发送更平滑,减少队列与丢包。

QUIC 在用户态做拥塞控制,ACK 更精确、可调频,连接级拥塞 + 流级流控 + 内置 pacing,是相对 TCP 的改进。

#
★★

9. QUIC 的 Initial、Handshake、0-RTT、1-RTT 包分别使用哪一阶段密钥,何时可承载应用数据?

请说明 QUIC 的 Initial、Handshake、0-RTT、1-RTT 包分别使用哪一阶段的密钥,以及何时可承载应用数据?

  • QUIC 各包类型与密钥阶段
  • 密钥派生(Initial/Handshake/Application)
  • 应用数据承载时机

QUIC 握手使用 TLS 1.3 分层密钥:Initial 包使用 Initial 密钥(由公开的 Initial Secret 派生,仅用于保护握手初期,可被对端解密,防中间人读取但非最终安全);Handshake 包使用 Handshake 密钥(在首次握手密钥交换后派生,保护握手中期);0-RTT 包使用 0-RTT 密钥(由缓存的 PSK 派生,客户端在 1-RTT 握手完成前即可发送早期应用数据,但存在重放风险);1-RTT 包使用应用数据密钥(Application/1-RTT 密钥,握手完成后派生,用于保护正式应用数据)。何时承载应用数据:0-RTT 在握手完成前允许客户端发送早期应用数据(需重放防护);1-RTT 在握手完成后(服务端确认)可承载正式应用数据并保证安全。

密钥随握手阶段逐步升级:Initial→Handshake→Application。0-RTT 提前发数据但安全性/重放受限,1-RTT 是正式安全通道。

#
★★

10. 连接 ID 如何支持 NAT 重绑定与路径迁移,为什么不能仅依赖四元组识别连接?

请说明 QUIC 的连接 ID(Connection ID)如何支持 NAT 重绑定与路径迁移,以及为何不能仅依赖四元组识别连接?

  • 连接 ID 与连接标识
  • NAT 重绑定与路径迁移
  • 四元组的局限性

QUIC 用连接 ID(Connection ID)标识连接,而非 4 元组(源 IP/端口、目的 IP/端口)。当客户端 NAT 重绑定(如 Wi-Fi 切蜂窝,源 IP 变化)或路径迁移(换网络)时,4 元组会改变,但连接 ID 不变,对端仍能通过连接 ID 识别同一连接,从而保持连接不断。若仅依赖四元组,源 IP/端口一变化就无法对应原连接,连接会中断。NAT 重绑定时,客户端用新源地址发送带原连接 ID 的包,对端依据连接 ID 更新地址映射并继续;路径迁移(连接迁移)时也通过连接 ID 维持。因此连接 ID 保证了"连接身份与地址解耦",使 NAT 重绑定与路径迁移成为可能。

连接 ID 是"连接身份",4 元组是"当前地址"。地址变化(NAT/迁移)时连接 ID 不变,才能保持连接。这是 QUIC 相对 TCP 的关键优势之一。

#
★★

11. QUIC 的 stream 多路复用为何仍需要连接级拥塞控制,流控与连接控窗口的协作机制是什么?

请说明 QUIC 的 stream 多路复用为何仍需要连接级拥塞控制,以及流控与连接级窗口的协作机制?

  • 连接级拥塞控制的原因
  • 流级流控与连接级流控
  • 窗口协作

即使 QUIC 支持多个 stream 多路复用,多条 stream 共享同一网络路径,拥塞由路径决定(瓶颈链路),因此拥塞控制必须是连接级的:所有 stream 共享一个连接级拥塞窗口,避免单条流或多个流超出路径容量导致拥塞。若每条流独立拥塞控制,会相互竞争、总流量超限。流控与连接级窗口协作:QUIC 有流级流控(每个 stream 有独立的接收窗口,限制该流数据量)和连接级流控(连接总接收窗口,限制所有流总量)。发送端实际发送量由"连接级拥塞窗口"(网络容量)与"流级/连接级接收窗口"(接收方背压)共同决定,取较小者。即:既受路径拥塞限制(连接级拥塞窗口),也受接收方缓冲限制(流级+连接级流控窗口),两者协作防止拥塞与接收方溢出。

拥塞是"网络路径"问题(连接级),流控是"接收方缓冲"问题(流级+连接级)。两者取小共同决定发送量,保证既不拥塞也不溢出。

#
★★

12. QUIC 每个流独立排序如何消除 TCP 层队头阻塞,丢包仍会造成哪些连接级影响?

请说明 QUIC 每个流独立排序如何消除 TCP 层的队头阻塞,以及丢包仍会造成哪些连接级影响?

  • 流级独立排序与队头阻塞
  • 连接级影响(窗口、重传)
  • HOL 阻塞的消除

TCP 是单字节流,若一个段的包丢失,后续所有已收数据(无论属于哪个应用逻辑流)都要等重传,造成队头阻塞(HOL)。QUIC 把数据分成多个 stream,每个 stream 独立编号与排序:某条 stream 丢包只阻塞该 stream 的乱序重传,不影响其他 stream 的已收数据向上交付,从而消除跨流的队头阻塞。但丢包仍会造成连接级影响:1) 拥塞控制是连接级的,丢包会触发连接级窗口减小、重传,所有流共享的发送速率下降;2) 连接级流控/重传队列共享,丢包占用重传缓冲;3) 若丢包是连接级问题(如连接 CID 迁移、致命错误),可能影响整个连接。因此流级消除了"应用层 HOL",但网络级拥塞/重传仍是连接级的。

流级独立排序消除"流间队头阻塞",但丢包导致的拥塞窗口收缩与重传是连接级的,所有流共享。这是"流级消除 HOL、连接级共享拥塞"的区别。

#
★★

13. QUIC 握手阶段的 TLS 1.3 对证书压缩、签名算法(Ed25519/RSA-PSS)和密钥派生上的约束是什么?

请说明 QUIC 握手阶段 TLS 1.3 对证书压缩、签名算法(Ed25519/RSA-PSS)和密钥派生上的约束?

  • TLS 1.3 与 QUIC 的握手
  • 证书压缩与签名算法
  • 密钥派生(HKDF)

QUIC 内嵌 TLS 1.3 握手,利用 TLS 1.3 的流量密钥与证书机制。证书压缩:TLS 1.3 支持证书压缩(Certificate Compression),QUIC 握手包需尽量小,压缩证书可减少握手包大小,但需双方协商压缩算法。签名算法:TLS 1.3 移除了旧算法(如 RSA-PKCS1v1.5 签名、SHA-1),只允许现代签名(Ed25519、RSA-PSS、ECDSA 等),QUIC 遵守 TLS 1.3 的签名算法约束,Ed25519 因高效、小签名校验快速而受青睐,RSA-PSS 兼容性广。密钥派生:TLS 1.3 用 HKDF(HKDF-Extract/Expand)从共享密钥派生握手密钥、应用密钥和 QUIC 的各阶段密钥(Initial/Handshake/Application),QUIC 据此分层派生各类密钥,并支持 Key Update(重新派生密钥)。约束:QUIC 强制 TLS 1.3(不接受 TLS 1.2),且使用 TLS 1.3 的密钥架构与 ALPN。

QUIC 用 TLS 1.3 的握手与密钥体系,证书压缩减小握手、现代签名算法保证安全、HKDF 派生分层密钥。这些约束由 TLS 1.3 规范与 QUIC 集成决定。

#
★★

14. QUIC 连接迁移(connection migration)触发后,新的路径 CID 如何通知对端,路径验证(path validation)为何必须?

请说明 QUIC 连接迁移触发后新的路径连接 ID(CID)如何通知对端,以及路径验证(path validation)为何必须?

  • 连接迁移与 CID 变更
  • 路径验证(PATH_CHALLENGE/PATH_RESPONSE)
  • 防攻击与地址确认

QUIC 连接迁移(如客户端换网络)后,客户端会用新的源地址发送数据,并可能携带新的目的连接 ID(DCID)或通过 NEW_CONNECTION_ID 帧让对端更新连接的 CID。对端通过收到的包识别迁移,用新的 CID 继续通信。路径验证(path validation)必须进行:对端发送 PATH_CHALLENGE(含随机 token),客户端从新路径回 PATH_RESPONSE,对端以此确认新路径真实可达且客户端确实拥有该地址,防止攻击者伪造源地址发起攻击(如把流量定向到受害地址、放大攻击)。路径验证确保"新地址是真实客户端所有",迁移后才切换主路径,避免地址欺骗。

迁移用 CID 保持连接身份,路径验证用挑战/响应确认新地址真实。路径验证是防地址伪造与放大攻击的关键,也是迁移的安全前提。

#
★★

15. 部署 UDP/QUIC 后握手超时升高时,应如何验证 MTU、负载均衡 CID 路由与 UDP 会话超时?

请说明部署 UDP/QUIC 后握手超时升高时,应如何验证 MTU、负载均衡的连接 ID 路由与 UDP 会话超时?

  • QUIC 握手超时与 MTU
  • 负载均衡的 CID 路由
  • UDP 会话超时

QUIC 握手超时升高,排查三点:1) MTU——QUIC 握手包(Initial)较大,若路径 MTU 过小且 UDP 分片被丢弃,握手包丢失→超时,验证各路径 MTU 与分片(用 ping 测 MTU、检查 UDP 分片);2) 负载均衡 CID 路由——QUIC 用连接 ID 标识连接,L4 负载均衡器必须按 CID 而非 4 元组做会话保持,若负载器按 4 元组或 CID 轮转错误,同一连接的握手包被分到不同后端→握手失败,验证负载器是否识别并稳定路由 CID;3) UDP 会话超时——UDP 无状态,负载器/防火墙的 UDP 会话老化太快会早于 QUIC 空闲超时切断会话,导致后续包丢失,验证 UDP 会话超时表与 QUIC 的 keepalive/PING 帧。

握手超时是"包没到/会话被切"的体现。MTU 影响包能否到达,CID 路由影响连接一致性,UDP 会话超时影响长空闲连接,三者需逐一验证。

#
★★

16. HTTP/3 在负载均衡设备上需识别 QUIC 报文头才能正确分片,运营商封禁 UDP 443 后回退到 HTTP/2 的策略如何实现?

请说明 HTTP/3 在负载均衡设备上为何需识别 QUIC 报文头才能正确分片,以及运营商封禁 UDP 443 后回退到 HTTP/2 的策略如何实现?

  • HTTP/3 与 QUIC 报文头识别
  • 负载均衡的分片/CID 路由
  • UDP 443 被封后的回退机制

HTTP/3 运行在 QUIC(UDP)之上,负载均衡器要正确分片/保持会话,必须识别 QUIC 报文头以提取连接 ID(CID),因为 QUIC 不用 4 元组标识连接,且客户端可能换源地址(NAT 迁移)。若负载器只解析 4 元组,无法稳定路由 QUIC 连接,也无法进行按 CID 的会话保持或分片。封禁 UDP 443 后回退到 HTTP/2:客户端在尝试 HTTP/3(UDP 443)失败/超时后,通过 ALPN 协商或降级机制回退到 HTTP/2(TCP 443)。实现:应用/协议栈检测 UDP 443 不可达(超时、ICMP 端口不可达)后,改用 HTTP/2 over TCP;或使用 Alt-Svc 头里声明的 h3 失败时回退;也可用 quic-go/nghttp3 等库的 fallback。需要客户端与服务端都支持 HTTP/2 备选,且在 QUIC 握手超时后自动降级。

QUIC 用 CID 标识连接,负载器需解析 QUIC 头提取 CID 才能正确分片/保持。封禁 UDP 443 时,靠 ALPN 协商与超时降级回退到 HTTP/2 over TCP。

#
★★

17. CUBIC 的窗口增长函数(W = C(t-K)^3 + W_max)如何实现 TCP 友好与高带宽利用,与 Reno 的 AIMD 有何本质差异?

请说明 CUBIC 的窗口增长函数 W = C(t-K)^3 + W_max 如何实现 TCP 友好与高带宽利用,以及与 Reno 的 AIMD 的本质差异?

  • CUBIC 的立方增长函数
  • 高带宽利用率与 TCP 友好
  • 与 AIMD 的本质差异

CUBIC 用立方函数 W = C(t-K)^3 + W_max 增长窗口,其中 C 是比例系数,K 是到达 W_max 所需的时间,W_max 是上次拥塞时的窗口。丢包后窗口降到 beta*W_max,然后从 W_max 附近快速恢复(立方增长在接近 W_max 时增速快),并利用 W_max 作为"上次瓶颈"的参考,在瓶颈附近平稳增长。与 AIMD 的本质差异:Reno 用 AIMD(加性增、乘性减),窗口线性增长,与 RTT 相关,高带宽×高延迟(BDP 大)时恢复慢、利用率低;CUBIC 用立方增长,与 RTT 无关(独立于 RTT 的窗口增长),在长 RTT 高带宽下恢复更快、利用率更高,同时通过让窗口在 W_max 附近趋缓(t 趋近 K 时增速变小)实现 TCP 友好(与其他流公平、不抢占)。

CUBIC 的核心是"立方增长 + 以 W_max 为锚点",独立于 RTT、恢复快、利用率高,且接近 W_max 时增速放缓体现公平。这是相对 Reno AIMD 的本质改进。

#
★★

18. 当 QUIC 应用出现抖动时,是流级别乱序还是连接级别乱序,二者在 PADDING/PING 帧探测时的区别是什么?

请说明 QUIC 应用出现抖动时,是流级别乱序还是连接级别乱序,以及二者在 PADDING/PING 帧探测时的区别?

  • 流级与连接级乱序
  • 乱序检测与重传
  • PADDING/PING 帧探测

QUIC 的乱序既有流级也有连接级:网络乱序是连接级的(同一连接的所有包在网络上乱序),而"流级排序"是接收端按流独立重排交付。抖动(延迟变化)多源于网络(连接级路径变化、队列、重传),表现为某流或连接整体延迟波动。PADDING/PING 帧探测:PING 帧是 ack-eliciting 的探测帧,用于触发对端回 ACK 以测量 RTT、探测路径是否异常(如网络抖动、路径切换);PADDING 帧用于填充包达到最小尺寸(如放大限制、探测 MTU),本身不产生应用数据也不触发 ACK(除非与 PING 一起)。区别:PING 探测"路径是否可达、RTT 多大"(连接级,用于检测抖动/迁移),PADDING 用于"包大小/MTU/放大约束"填充,两者一个是活性/时延探测,一个是尺寸填充。

抖动是路径/连接级现象,流级只是重排。PING 帧做活性与 RTT 探测(连接级),PADDING 帧做尺寸填充(MTU/放大),两者用途不同。

#
★★

19. QUIC 不可恢复错误码 CONNECTION_CLOSE 的 application vs transport 分类如何指导调试,关闭帧延迟传递的语义?

请说明 QUIC 不可恢复错误码 CONNECTION_CLOSE 的 application 与 transport 分类如何指导调试,以及关闭帧延迟传递的语义?

  • CONNECTION_CLOSE 的 application/transport 错误码
  • 调试定位
  • 关闭帧延迟传递

QUIC 的 CONNECTION_CLOSE 帧携带错误码,分为传输层错误(transport error,如 0x00-0x1C 的协议错误、流量控制错误、帧编码错误)和应用层错误(application error,由应用定义,如 HTTP/3 的 H3_* 错误码)。分类指导调试:transport 错误说明是 QUIC 传输层本身的问题(版本、帧、流控、加密等),需查协议实现与网络层;application 错误说明传输层正常、是应用协议(如 HTTP/3)层面的错误,需查应用逻辑。关闭帧延迟传递语义:CONNECTION_CLOSE 携带错误信息后,对端可能已在本端发送关闭前收到部分数据,存在"延迟传递"——即关闭帧可能被延迟/经过重传,发送方需确保关闭信息可靠送达(如等待对端 ACK 或超时),且某些错误码(如 FINAL_SIZE 错误)指向之前的流状态,需结合已处理数据的上下文诊断。

application/transport 错误码区分"传输层"与"应用层"故障,指导调试方向。关闭帧延迟传递强调关闭信息的可靠送达与错误上下文的关联。

#
★★

20. gRPC over QUIC 相比 gRPC over HTTP/2 在多路复用与流控粒度上的差异,stream ID 与 http2 stream 兼容性问题?

请说明 gRPC over QUIC 相比 gRPC over HTTP/2 在多路复用与流控粒度上的差异,以及 stream ID 与 HTTP/2 stream 的兼容性问题?

  • HTTP/2 与 QUIC 的多路复用
  • 流控粒度
  • stream ID 兼容性

gRPC 在 HTTP/2 上运行,多路复用由 HTTP/2 的 stream 承载,流控是连接级+流级(HTTP/2 的流控帧)。gRPC over QUIC 时,HTTP/3 把 HTTP/2 的 stream 映射到 QUIC 的 stream,多路复用由 QUIC 流实现,流控是 QUIC 的流级+连接级流控(更精细)。差异:QUIC 流级独立排序消除队头阻塞,HTTP/2 有 TCP 层 HOL;QUIC 流控粒度更细且无 TCP 层耦合。兼容性问题:HTTP/2 的 stream ID 是由 HTTP/2 层分配的(奇数/偶数标识客户端/服务端发起),HTTP/3 把 stream 语义映射到 QUIC stream ID,但 HTTP/3 的 stream 类型与 HTTP/2 不同(如控制流、请求流、推送流),QUIC 的 stream ID 是独立的(0 为控制流、客户端发起的请求流 ID 奇偶分配等),因此 gRPC over HTTP/3 需重新映射 stream 语义,与 HTTP/2 的 stream ID 不直接兼容,需通过 HTTP/3 的帧层适配。

QUIC 提供流级多路复用与精细流控,消除 TCP HOL;HTTP/3 的 stream 语义与 HTTP/2 不同(不同类型流、不同 ID 分配),gRPC 跨版本需适配。

#
★★

21. send() 把数据拷贝进 socket 发送缓冲区后即可返回,真正的分段、重传由内核哪条路径完成?发送缓冲区满时阻塞与非阻塞语义有何不同?

请说明 send() 把数据拷贝进 socket 发送缓冲区后即可返回,真正的分段、重传由内核哪条路径完成,以及发送缓冲区满时阻塞与非阻塞语义有何不同?

  • send() 与发送缓冲区
  • 内核分段/重传路径
  • 阻塞与非阻塞语义

send() 把用户数据拷贝到内核 socket 发送缓冲区后即可返回(拷贝成功),真正的打包、分段(TCP 分段/分片)、拥塞控制、重传由内核的 TCP 栈(tcp_write_xmit、tcp_transmit_skb 等)在后续的发送路径完成,即内核在合适时机把 skb 交给 IP 层/网卡并管理重传。阻塞 vs 非阻塞语义:发送缓冲区满时,阻塞 send() 会睡眠等待缓冲区有空间(或超时),直到数据可拷贝;非阻塞 send()(O_NONBLOCK)立即返回 EAGAIN/EWOULDBLOCK,提示缓冲区满,应用需重试或使用 I/O 多路复用等待可写。二者区别是"是否等待":阻塞等待空间,非阻塞不等待直接返回错误。

send() 只负责"拷贝进缓冲区",真正的网络发送(分段/重传/拥塞)由内核异步完成。缓冲区满时阻塞等待、非阻塞返回 EAGAIN 是核心语义差异。

#
★★

22. 接收方数据从网卡硬中断、NET_RX 软中断到 socket 接收队列经历哪些步骤?阻塞 recv() 何时被唤醒?

请说明接收方数据从网卡硬中断、NET_RX 软中断到 socket 接收队列经历的步骤,以及阻塞 recv() 何时被唤醒?

  • 硬中断与软中断(NAPI、NET_RX_SOFTIRQ)
  • 数据入 socket 接收队列
  • 阻塞 recv() 唤醒时机

接收流程:网卡收到数据后产生硬中断(IRQ),内核(NAPI 机制)在硬中断中快速收包并调度 NET_RX 软中断;NET_RX 软中断中处理数据包,经协议栈(IP/TCP 处理、校验、重组到 skb)后,数据被放入 socket 的接收队列(sk_receive_queue)。阻塞 recv() 的唤醒:当数据包到达、被放入 socket 接收队列并更新 socket 状态(增加接收字节)时,内核唤醒等待在该 socket 上的阻塞进程(通过 wake_up 唤醒 recv 的等待队列),使其从 recv() 返回数据。若数据不足,recv() 会继续等待直到有足够数据或对端关闭。

数据经"硬中断→软中断→协议栈→socket 接收队列"再到应用。阻塞 recv() 在数据进入接收队列时被唤醒,这是接收路径与应用唤醒的衔接点。

#
★★

23. sendfile() 相比 read()+write() 省去了哪几次拷贝与上下文切换?支持 DMA gather 的网卡如何进一步消除 CPU 拷贝?

请说明 sendfile() 相比 read()+write() 省去了哪几次拷贝与上下文切换,以及支持 DMA gather(SG-DMA)的网卡如何进一步消除 CPU 拷贝?

  • read()+write() 的多拷贝与上下文切换
  • sendfile() 的零拷贝
  • DMA gather 消除 CPU 拷贝

read()+write() 需要:read 从磁盘/网络读入内核缓冲区(DMA 拷贝),再拷贝到用户态缓冲区(CPU 拷贝),write 把用户态数据拷贝到内核 socket 缓冲区(CPU 拷贝),再经 DMA 发出,共 4 次拷贝(2 次 DMA + 2 次 CPU 拷贝)和多次上下文切换(系统调用切换)。sendfile() 直接把数据在文件与 socket 之间传输,避免用户态参与:数据从磁盘 DMA 到内核 page cache,再(若网卡支持 SG-DMA)直接通过 DMA 从 page cache 发送到网卡,无需 CPU 拷贝到用户态,也减少了上下文切换(一次系统调用)。支持 DMA gather 的网卡(如支持 SG 的 NIC)能直接从内核的多个分散页面(page cache)收集数据并通过 DMA 发送,无需先把数据拷贝到连续缓冲区,从而彻底消除 CPU 拷贝,实现真正的零拷贝传输。

sendfile 省去的是"用户态往返"的 CPU 拷贝与系统调用切换;SG-DMA 让网卡直接从 page cache 分散页 DMA 发送,进一步消除 CPU 拷贝。

#
★★

24. splice() 借助管道在两个文件描述符间传输数据为何能避免用户态拷贝?pipe buffer 在其中起什么作用?

请说明 splice() 借助管道在两个文件描述符间传输数据为何能避免用户态拷贝,以及 pipe buffer 在其中起什么作用?

  • splice() 与管道
  • 零拷贝(page 引用)
  • pipe buffer 的角色

splice() 在两个文件描述符之间传输数据,通过中间管道实现,关键是不把数据拷贝到用户态:它把源文件的页(page)引用(而不是数据副本)放入管道的 pipe buffer,再从管道读出到目标 fd,全程在内核空间操作 page 引用,避免用户态拷贝。pipe buffer 是管道内部的一批"缓冲区描述符",每个指向一个页(或页内偏移),splice 把源页的引用挂到 pipe buffer 上,目标端从 pipe buffer 取页引用直接发送/写入,无需复制数据。因此 splice 通过"页引用传递"实现零拷贝,pipe buffer 扮演"页引用中转站"。适合需要零拷贝的场景(如文件到 socket、socket 到文件)。

splice 的核心是"传页引用而非数据",pipe buffer 持有这些页引用以实现中转。这避免了用户态拷贝,是零拷贝的另一种实现。

#
★★

25. SO_REUSEPORT 允许多个 socket 绑定同一端口,内核如何按 4-tuple 哈希分发连接,保证同一连接始终落到同一进程?

请说明 SO_REUSEPORT 允许多个 socket 绑定同一端口,内核如何按 4 元组哈希分发连接,并保证同一连接始终落到同一进程?

  • SO_REUSEPORT 与多 socket 绑定
  • 4 元组哈希分发
  • 连接一致性(同一连接同一进程)

SO_REUSEPORT 允许多个进程/线程绑定同一 IP:端口,内核在监听时按"连接四元组哈希"把新连接分发到某个 socket 上。分发用 4 元组(源 IP、源端口、目的 IP、目的端口)做哈希,取模后选择监听 socket,从而同一连接(相同 4 元组)总是被哈希到同一个 socket/进程,保证同一连接的所有数据落到同一进程,避免连接被拆分。同时哈希使不同连接分散到不同进程,实现负载均衡。内核用 SO_REUSEPORT 的 socket 组及哈希表(reuseport group)维护,按哈希均匀选择。这使多进程/多线程服务器可共享监听端口并均衡分摊连接。

SO_REUSEPORT 用"4 元组哈希 + 选择"把连接分发到指定 socket,按哈希保证同一连接同一进程,并按哈希均衡分布。这是多进程多核扩展的关键。

#
★★

26. TCP_NODELAY 关闭 Nagle、TCP_CORK 累积数据的语义分别是什么?它们与延迟 ACK 交互时如何影响小包延迟?

请说明 TCP_NODELAY 关闭 Nagle、TCP_CORK 累积数据的语义分别是什么,以及它们与延迟 ACK 交互时如何影响小包延迟?

  • Nagle 算法与 TCP_NODELAY
  • TCP_CORK 累积发送
  • 与延迟 ACK 的交互

Nagle 算法规定:发送缓冲区有未确认数据时,小包需等待凑满一个 MSS 或等到 ACK 才发送,以减少小包数量。TCP_NODELAY 关闭 Nagle,让每个小包立即发送,降低小包延迟(适合交互式应用)。TCP_CORK 是"塞住"用于累积数据:设置了 CORK 后,内核不立即发送小包,而是等到累积足够或取消 CORK 才发送,用于批量发送减少小包。与延迟 ACK 交互:延迟 ACK 让接收方延迟 40ms 才回 ACK,若发送端用 Nagle 等待 ACK 凑包、接收端延迟 ACK,会形成"相互等待"(Nagle 等 ACK、延迟 ACK 等数据),造成小包延迟升高(约 40ms 的延迟);TCP_NODELAY 关闭 Nagle 可打破此僵局,但增加了小包数量。因此小包延迟场景通常用 TCP_NODELAY 配合关闭延迟 ACK 或调整。

Nagle 凑包、CORK 累积、延迟 ACK 是三类"减少小包/延迟"的机制,交互时可能形成"等待环"导致延迟。TCP_NODELAY 关闭 Nagle 打破僵局。

#
★★

27. SO_RCVBUF/SO_SNDBUF 与内核自动调优(tcp_rmem/tcp_wmem)是什么关系?为何显式设置后会关闭自动调优?

请说明 SO_RCVBUF/SO_SNDBUF 与内核自动调优(tcp_rmem/tcp_wmem)的关系,以及为何显式设置后会自动关闭自动调优?

  • SO_RCVBUF/SO_SNDBUF 与自动调优
  • tcp_rmem/tcp_wmem 的 min/default/max
  • 显式设置关闭自动调优

内核默认用 tcp_rmem/tcp_wmem 的 min/default/max 三档参数自动调优 socket 缓冲区:自动调优时内核根据连接 RTT 与 BDP 动态调整缓冲区大小(在 min 和 max 之间)。SO_RCVBUF/SO_SNDBUF 由应用显式设置缓冲区大小。关系:若应用用 SO_RCVBUF/SO_SNDBUF 显式设置(且未设 SO_RCVBUFFORCE/SO_SNDBUFFORCE 时),内核会关闭自动调优,把缓冲区固定为该值(并受 max 上限约束)。原因:自动调优需要一个"自由调整"的窗口,应用显式指定缓冲区说明应用有明确需求,内核不应再动态覆盖,故关闭自动调优,尊重应用设置。若想让内核自动调优,应不设置 SO_RCVBUF/SO_SNDBUF(或设为 0)。

自动调优是内核按 BDP 动态调,显式 SO_RCVBUF/SO_SNDBUF 表明应用意图,内核关闭自动调优以避免冲突。这是"内核启发式 vs 应用控制"的关系。

#
★★

28. shutdown() 与 close() 对 TCP 连接状态、引用计数与未读数据的影响有何不同?

请说明 shutdown() 与 close() 对 TCP 连接状态、引用计数与未读数据的影响有何不同?

  • shutdown()(半关闭)与 close()(全关闭)
  • 引用计数
  • 未读数据的影响

close() 关闭 socket 的全部方向,发送 FIN 并释放 socket(需引用计数归零才真正释放),对未读数据,close() 会丢弃未读的接收数据(且发送 RST 的可能性取决于 socket 状态,若接收缓冲区有未读数据,close 可能发 RST 而非 FIN)。shutdown() 只做半关闭:shutdown(WR) 停止发送(发 FIN 但保留接收),shutdown(RD) 停止接收(丢弃后续数据但保留发送),不释放 socket 也不影响引用计数。引用计数:close() 递减引用计数,归零才真正关闭;shutdown() 不改变引用计数,只改变连接的使用方向。未读数据:close() 会丢弃未读数据;shutdown(RD) 丢弃未继续读的数据,但 socket 仍可发送。因此 shutdown 用于"半关闭"的优雅语义,close 用于"完全关闭"。

close 是"全关+释放",shutdown 是"半关+保留",且 close 受引用计数、shutdown 不受。未读数据上 close 丢弃、shutdown 按方向处理。

#
★★

29. BBR 与 CUBIC 在瓶颈带宽测量模型上的差异,BBR2/BBR3 解决了哪些公平性问题?

请说明 BBR 与 CUBIC 在瓶颈带宽测量模型上的差异,以及 BBR2/BBR3 解决了哪些公平性问题?

  • BBR 的模型(带宽×延迟乘积)
  • CUBIC 的丢包驱动
  • BBR2/BBR3 的公平性改进

CUBIC 是"丢包驱动"的拥塞控制:以丢包为拥塞信号,通过窗口增长/下降逼近瓶颈,虽吞吐高但依赖丢包,且在高带宽延迟乘积(BDP)下受丢包门限影响。BBR 是"模型驱动"的:显式测量瓶颈带宽(BtlBw)与最小 RTT(RTprop),窗口≈BtlBw×RTprop(BDP),通过 pacing 以瓶颈带宽发送,不依赖丢包,能充分利用带宽、降低队列与延迟。BBR 的问题:初始/公平性上,BBR 对共享瓶颈的多个流可能不收敛公平(带宽分配不均、对 CUBIC 流不友好)。BBR2/BBR3 改进:BBR2 引入丢包/ECN 感知的拥塞避让,在丢包时适度降速,改善与 CUBIC 的共存与公平;BBR3 进一步优化多流收敛、降低不公平性,改善初始带宽探测与带宽分配的公平性。核心是 BBR 基于模型、CUBIC 基于丢包,BBR2/3 修复模型的公平性缺陷。

差异是"模型驱动(BtlBw×RTprop)"vs"丢包驱动"。BBR 吞吐高但公平性差,BBR2/3 通过丢包/ECN 感知与多流收敛改进公平。

#
★★

30. 0-RTT 数据在重放攻击场景下的应用限制(safe/unsafe data),客户端如何避免被回放污染业务状态?

请说明 QUIC 0-RTT 数据在重放攻击场景下的应用限制(safe/unsafe data),以及客户端如何避免被回放污染业务状态?

  • 0-RTT 的重放风险
  • safe/unsafe data 区分
  • 防重放(幂等、nonce、应用层防护)

QUIC 0-RTT 允许客户端在握手完成前用缓存的会话参数发送早期数据,但 0-RTT 数据可被攻击者重放(重复发送相同的 0-RTT 包),因为 0-RTT 密钥基于 PSK 且无防重放机制。因此 0-RTT 只能承载"安全重放"的数据(safe/replayable,如幂等查询、缓存获取、可重复的请求),不能承载"不安全重放"的数据(unsafe,如支付、转账、状态变更、会产生副作用的操作),服务器应拒绝在 0-RTT 中处理非幂等请求。客户端避免被回放污染的机制:服务端对 0-RTT 请求做幂等性校验(如 ID 去重、nonce 校验、时间戳),对非幂等操作强制使用 1-RTT;客户端在 0-RTT 中只发送幂等/只读数据,把状态变更放到 1-RTT;服务端可限制 0-RTT 的请求类型与重放窗口(如用 anti-replay 数据库)。

0-RTT 的代价是重放攻击,故只能承载幂等/安全的请求。防重放靠"幂等性 + 服务端去重 + 限制 0-RTT 范围"。

#
★★

31. epoll 边缘触发只在状态跳变时通知一次,为什么必须用非阻塞 fd 并循环读到 EAGAIN?漏读会造成什么后果?

请说明 epoll 边缘触发(ET)只在状态跳变时通知一次,为何必须用非阻塞 fd 并循环读到 EAGAIN,以及漏读会造成什么后果?

  • 边缘触发(ET)与水平触发(LT)
  • 非阻塞 fd 与循环读
  • 漏读后果

epoll 边缘触发(ET)只在"文件描述符状态从无数据变为有数据"(跳变)时通知一次,之后不再通知,直到再次发生状态跳变。因此 ET 模式下必须把 fd 设为非阻塞,并在触发后循环 read 直到返回 EAGAIN(表示读到暂时无数据),以一次性读完所有可用数据。若漏读(读到 EAGAIN 前就停止,或没读干净),残留数据不会再触发通知(因为状态未跳变),导致数据滞留、事件丢失,应用可能永远等不到下一次通知,造成读不到数据或连接卡死。这是 ET 的高效(减少事件通知次数)与严格(必须读尽)之间的权衡。

ET 是"事件驱动",只通知跳变,必须非阻塞 + 循环读到 EAGAIN 才能读尽。漏读会因无后续通知而滞留在 fd 中,是 ET 的经典陷阱。

#
★★

32. 接收窗口与拥塞窗口分别限制什么,发送端实际在途数据上限如何确定?

请说明接收窗口(rwnd)与拥塞窗口(cwnd)分别限制什么,以及发送端实际在途数据上限如何确定?

  • 接收窗口(rwnd)与拥塞窗口(cwnd)
  • 在途数据上限
  • 取小原则

接收窗口(rwnd)由接收方通告,限制"接收方缓冲区能接收的数据量",反映接收端背压;拥塞窗口(cwnd)由发送端根据网络拥塞动态调整,限制"网络路径允许发送的数据量",反映网络容量的背压。发送端实际在途数据(未确认数据)上限 = min(cwnd, rwnd):即既不超过网络容量(cwnd),也不超过接收方缓冲区(rwnd),取两者较小者。若数据超过 rwnd,接收方缓冲区溢出;超过 cwnd,网络拥塞。因此发送窗口(swnd)= min(cwnd, rwnd),决定发送端一次能发送的最大未确认数据量。

cwnd 管"网络",rwnd 管"接收方",实际在途上限取二者最小值。这是"同时受网络与接收方背压"的体现。

#
★★

33. ACK clocking,为什么拥塞窗口通常按 ACK 到达节奏增长,突发发送与 pacing 对瓶颈队列与丢包的影响如何?

请说明 ACK clocking 为何使拥塞窗口通常按 ACK 到达节奏增长,以及突发发送与 pacing 对瓶颈队列与丢包的影响?

  • ACK clocking 机制
  • 窗口增长与 ACK 节奏
  • 突发 vs pacing

ACK clocking(ACK 时钟)指 TCP 的发送节奏由到达的 ACK 驱动:每个已确认的 ACK 推动发送窗口前移,发送端收到 ACK 后才能发送新数据,因此发送速率被"回程的 ACK 流"自然计时,与网络往返匹配,形成自同步的时钟。拥塞窗口按 ACK 到达节奏增长(每 RTT 增加一定量),使发送跟随网络能力。突发发送(burst)会一次性发送大量包,在瓶颈队列形成突发堆积,导致队列膨胀、延迟增加与丢包(尾部丢弃);pacing 则把数据按速率均匀发送,避免突发,使瓶颈队列保持低且稳定,减少丢包与抖动。因此 pacing 平滑发送可改善队列与丢包,是 ACK clocking 的补充。

ACK clocking 让发送跟随回程节奏,突发是"突发发送"的堆积,pacing 是"均匀发送"的平滑。pacing 降低队列与丢包。

#
★★

34. TCP Fast Open(TFO)如何用 TFO cookie 减少建连 RTT,与 QUIC 0-RTT 在重放防护语义上的差异?

请说明 TCP Fast Open(TFO)如何用 TFO cookie 减少建连 RTT,以及它与 QUIC 0-RTT 在重放防护语义上的差异?

  • TFO cookie 与 SYN 携带数据
  • 减少 RTT
  • 与 QUIC 0-RTT 的重放防护差异

TCP Fast Open(TFO)允许客户端在首次获得服务端下发的 TFO cookie 后,在后续连接的 SYN 中携带应用数据,从而在三次握手期间就开始传输数据,减少一个 RTT(把首个请求数据提前到 SYN 中)。客户端需先缓存 cookie(服务端签发),重启后 SYN+数据 被服务端验证 cookie 后接受。重放防护差异:TFO 的 cookie 可被重放(SYN 携带的数据可被攻击者重放),但 TFO 数据受 SYN 重放限制,且 TFO 通常只用于幂等/安全请求,服务端对重放敏感度较低;QUIC 0-RTT 同样基于 PSK 可重放,但 QUIC 的 0-RTT 更明确地区分"安全/不安全数据",并可通过服务端限制 0-RTT 请求类型、审计重放窗口来防护。两者都面临重放,差异在于:TFO 的 cookie 是地址绑定的验证、重放风险相对受限;QUIC 0-RTT 用 PSK 加密、重放面更大,需更强的应用层幂等与防护语义。

TFO 和 QUIC 0-RTT 都提前发数据减少 RTT,都面临重放。TFO 靠 cookie 验证、QUIC 0-RTT 靠 PSK,后者重放面更大、需更明确的幂等/防护语义。

#
★★

35. SO_REUSEADDR、SO_KEEPALIVE 与 TCP_NODELAY 的语义与典型用法分别是什么?

请说明 SO_REUSEADDR、SO_KEEPALIVE 与 TCP_NODELAY 的语义与典型用法?

  • SO_REUSEADDR(地址复用)
  • SO_KEEPALIVE(心跳)
  • TCP_NODELAY(关闭 Nagle)

SO_REUSEADDR 允许一个 socket 绑定到"正在 TIME_WAIT 状态"的地址/端口,解决服务端重启后因 TIME_WAIT 无法绑定端口的问题,也允许同一端口多个 socket 绑定(与 SO_REUSEPORT 配合),典型用于服务端快速重启复用端口。SO_KEEPALIVE 开启 TCP 保活:空闲连接上内核周期发送 keepalive 探测包,检测对端是否仍存活(掉线/半开连接),典型用于清理长时间空闲的死连接(如服务器探测客户端是否还连着)。TCP_NODELAY 关闭 Nagle 算法,让小包立即发送,降低交互延迟,典型用于交互式应用(如 RPC、实时通信、需要低延迟的请求)避免小包被 Nagle 积压。

三者分别解决"端口复用""连接存活检测""小包延迟"问题,是网络编程常用的三个 socket 选项。

#
★★

36. QUIC 在 UDP 上为每个 stream 独立管理丢包重传,如何消除 TCP 单字节流上的队头阻塞?

请说明 QUIC 在 UDP 上为每个 stream 独立管理丢包重传,如何消除 TCP 单字节流上的队头阻塞?

  • 流级独立重传
  • TCP 单字节流的 HOL
  • 队头阻塞的消除

TCP 是单字节流,所有数据共享一个字节序列,若某一包丢失,接收端即使收到后续数据也不能交付(因为要按序),直到重传到达,造成队头阻塞(HOL)。QUIC 在 UDP 上把数据分成多个 stream,每个 stream 有独立的字节序列与重传管理:某条 stream 的包丢失,只重传该 stream 的缺失数据,其他 stream 的已收数据可立即向上交付,不受影响。因此从"单字节流"变为"多独立流",某流的丢包不再阻塞其他流,消除了跨流的队头阻塞。但同一流内仍按序(流内 HOL 仍存在),只是消除了流间 HOL。

QUIC 用"多流独立排序"替代"单字节流统一排序",把丢包的影响限定在单个流内,消除跨流队头阻塞。

#
★★

37. QUIC 连接以 Connection ID 而非 4-tuple 标识,连接迁移(如 Wi-Fi 切蜂窝)时如何保持连接不断?

请说明 QUIC 连接以 Connection ID 而非 4 元组标识,连接迁移(如 Wi-Fi 切蜂窝)时如何保持连接不断?

  • 连接 ID 标识连接
  • 连接迁移机制
  • 保持连接

QUIC 用连接 ID(CID)标识连接,而非 4 元组(源 IP/端口、目的 IP/端口)。当客户端从 Wi-Fi 切换到蜂窝(网络迁移),源 IP 和端口会改变,4 元组变化,但连接 ID 不变,对端仍能通过连接 ID 识别同一连接。迁移时客户端用新源地址发送带原连接 ID 的包,对端收到后更新该连接的地址映射,继续用连接 ID 通信。为平滑迁移,客户端会预先用 NEW_CONNECTION_ID 帧让对端提供新的 CID,迁移后使用新 CID;对端通过 PATH_CHALLENGE/PATH_RESPONSE 验证新路径后正式切换。因此连接 ID 使"连接身份与地址解耦",迁移时地址变但连接身份不变,连接不断。

连接 ID 解耦"连接身份"与"当前地址",网络迁移改变地址但连接 ID 不变,配合路径验证即可无缝迁移。

#
★★

38. QUIC 的 path validation(PATH_CHALLENGE/PATH_RESPONSE)在连接迁移中防止什么攻击?

请说明 QUIC 的 path validation(PATH_CHALLENGE/PATH_RESPONSE)在连接迁移中防止什么攻击?

  • PATH_CHALLENGE/PATH_RESPONSE
  • 地址伪造攻击
  • 防放大与防劫持

QUIC 的 path validation 用 PATH_CHALLENGE 帧(含随机 token)发送到新路径,对端必须从该路径回 PATH_RESPONSE(含相同 token),以此确认新路径真实可达且发送方确实拥有该地址。它防止的攻击:1) 地址伪造/欺骗——攻击者伪造源地址(如把受害者的 IP 当作源地址)诱导对端向受害者发送数据,PATH_CHALLENGE 要求从新地址回包,攻击者无法伪造受害者地址的响应,从而防止地址劫持与流量重定向;2) 放大攻击——未经验证就向新地址发送大量数据会被放大,path validation 确保只向已验证的地址发送数据,限制放大倍数。因此 path validation 是连接迁移中防地址伪造与放大攻击的关键。

PATH_CHALLENGE/RESPONSE 以"新地址必须能回包"验证地址真实性,防止地址欺骗、流量重定向与放大攻击。

#
★★

39. QUIC 0-RTT 允许客户端用缓存的会话参数提前发数据,为什么它天然面临重放攻击,服务端应如何限制?

请说明 QUIC 0-RTT 允许客户端用缓存会话参数提前发数据,为何天然面临重放攻击,以及服务端应如何限制?

  • 0-RTT 与重放
  • 0-RTT 密钥(PSK)
  • 服务端限制

QUIC 0-RTT 允许客户端在握手完成前用缓存的会话参数(PSK)派生 0-RTT 密钥,提前发送数据。0-RTT 密钥由 PSK 派生,无防重放机制,攻击者可以截获并重放相同的 0-RTT 包,服务端无法区分"原始"与"重放",因此 0-RTT 天然面临重放攻击。服务端限制:1) 只接受 0-RTT 中的幂等/安全请求,拒绝非幂等操作(如写入、支付)在 0-RTT 中处理;2) 用 anti-replay 机制(如记录已处理的 0-RTT 包、nonce/时间戳去重)检测重放;3) 限制 0-RTT 可发送的数据类型与请求范围,把敏感操作强制放到 1-RTT;4) 设置 0-RTT 重放窗口过期(如 0-RTT 数据在握手后立即失效)。通过这些限制降低重放带来的危害。

0-RTT 的加速以重放为代价,因为 PSK 派生密钥无防重放。服务端用"幂等性限制 + 去重 + 窗口过期"缓解。

#
★★

40. QUIC 把 TLS 1.3 握手内嵌到传输层,相比 TCP+TLS 如何减少建连往返(1-RTT/0-RTT)?

请说明 QUIC 把 TLS 1.3 握手内嵌到传输层,相比 TCP+TLS 如何减少建连往返(1-RTT/0-RTT)?

  • TCP+TLS 的握手往返
  • QUIC 内嵌 TLS 1.3
  • 1-RTT/0-RTT 减少

TCP+TLS(传统)需要:TCP 三次握手(1 RTT)+ TLS 1.3 握手(1 RTT),共 2 个 RTT 才能开始发应用数据;且 TLS 握手独立于 TCP 传输层。QUIC 把 TLS 1.3 握手内嵌到传输层(QUIC 的 Initial/Handshake 包携带 TLS 握手消息),握手与传输层建连合并:首次连接 QUIC 握手用 1-RTT(客户端 Initial 携带 ClientHello,服务端回应,一个往返后即可发应用数据),比 TCP+TLS 的 2 RTT 少一个 RTT。若客户端有缓存会话参数(PSK),QUIC 可用 0-RTT:客户端在握手完成前就发应用数据,0 RTT 即可开始(比 TCP+TLS 的 2 RTT 少两个 RTT)。因此 QUIC 内嵌 TLS 减少建连往返,0-RTT 是最快。

QUIC 把 TCP 握手与 TLS 握手合并,省去一个 RTT;0-RTT 用缓存 PSK 再省,实现 0 往返建连。

#
★★

41. QUIC 如何防御 UDP 反射放大攻击,address validation 与 amplification limit(放大倍数上限)的机制如何?

请说明 QUIC 如何防御 UDP 反射放大攻击,包括 address validation 与 amplification limit(放大倍数上限)的机制?

  • UDP 反射放大攻击
  • address validation(地址验证)
  • amplification limit(放大倍数限制)

UDP 无握手,攻击者可伪造源地址发送小请求,诱导服务端向受害地址发送大响应,形成反射放大攻击。QUIC 防御:1) address validation(地址验证)——在握手早期(Initial 阶段),服务端对客户端地址做验证,如通过 retry 机制(服务端发 Retry 帧带 token,客户端用 token 重发 Initial)确认客户端真实拥有该地址,防止伪造源地址;2) amplification limit(放大倍数限制)——服务端在未验证客户端地址前,发送给客户端的响应的总字节数不得超过"收到的请求字节数"的某个倍数(如 3 倍,RFC 9000 规定在 handshake 完成前,服务端发送数据量不超过收到的 3 倍),防止服务端被用于放大攻击。两者结合:地址验证确保来源真实,放大限制限制未验证阶段的响应量,从而抵御反射放大。

QUIC 靠"地址验证(Retry/token)"确认来源真实 + "放大倍数限制"控制未验证阶段的响应量,双管齐下防御 UDP 反射放大。

#
★★

42. QUIC 的流级流量控制与连接级流量控制如何配合,避免单个流耗尽整体资源?

请说明 QUIC 的流级流量控制与连接级流量控制如何配合,避免单个流耗尽整体资源?

  • 流级流控与连接级流控
  • 资源公平
  • 防止单流耗尽

QUIC 有流级流量控制(每个 stream 有独立的接收窗口,限制该流可发送的数据量)和连接级流量控制(整个连接有总接收窗口,限制所有流的数据总量)。协作机制:发送端发送数据时,既要满足该流自身的流级窗口(流级 MAX_STREAM_DATA),也要满足连接级总窗口(连接级 MAX_DATA),两者取小。这样,即使某条流想大量发送,也受其流级窗口限制,且受连接级总窗口限制,不会无限制占用;同时连接级窗口又防止多条流合计超过接收方总缓冲。因此流级+连接级双窗口避免单条流耗尽整体资源,保证多条流公平共享接收缓冲。

流级窗口限制单流,连接级窗口限制总体,取小决定发送,既防单流独占也防多流超限,是资源公平的关键。

#
★★

43. HTTP/3 把 HTTP 语义映射到 QUIC 流,控制流与请求流如何分工?

请说明 HTTP/3 把 HTTP 语义映射到 QUIC 流,以及控制流与请求流如何分工?

  • HTTP/3 的流类型
  • 控制流与请求流
  • 流分工

HTTP/3 把 HTTP 语义映射到 QUIC 流,流具有类型:控制流(Control Stream,QUIC stream 0)用于传输 HTTP/3 控制消息(如 SETTINGS、GOAWAY、PRIORITY_UPDATE 等),每连接一个;请求流(Request Stream)承载 HTTP 请求/响应(每个请求/响应用独立的 QUIC 流,HTTP/3 的请求/响应各自占一个 client-initiated 或 server-initiated 流);还可能有推送流(Push Stream,服务端推送)和 QPACK 编解码器流(用于动态表同步)。分工:控制流管理连接级状态(设置、优先级、优雅关闭),请求流承载具体的 HTTP 消息(请求/响应),QPACK 流管理头部压缩表的同步,推送流承载服务端推送。这样各类控制与数据分离,互不阻塞。

HTTP/3 用不同类型的 QUIC 流分工:控制流管连接状态、请求流传请求响应、QPACK 流管头部压缩、推送流传推送,各司其职。

#
★★

44. 为什么 QUIC 选择在用户态实现而非直接修改内核 TCP?这对协议迭代速度有何影响?

请说明 QUIC 为何选择在用户态实现而非直接修改内核 TCP,以及对协议迭代速度的影响?

  • 用户态实现的原因
  • 内核 TCP 修改的困难
  • 迭代速度

QUIC 选择在用户态(运行在 UDP 之上)实现,而非修改内核 TCP,原因:1) 部署与升级容易——用户态库(如 quiche、quic-go)可随应用发布,无需升级内核/系统,TCP 改动需内核升级、部署慢、兼容难;2) 可插拔与可编程——用户态可灵活实现可插拔拥塞控制、自定义头部、实验性特性,不受内核固定实现约束;3) 多平台一致——用户态跨平台一致,内核 TCP 各 OS 实现不同;4) 隔离与安全——用户态 bug 不影响内核稳定性。对协议迭代速度的影响:用户态实现使 QUIC 的迭代(拥塞控制、帧格式、扩展)以"应用更新"级别推进,无需等待内核发布,迭代速度远快于内核 TCP;QOS 特性可快速上线、回滚、A/B 测试,这是 QUIC 快速演进的关键。

用户态实现的核心是"部署灵活、可编程、跨平台",代价是 UDP 之上重造传输层(性能略降)。这让协议迭代以应用级速度进行。

#
★★

45. QUIC 按 packet number space 做丢包恢复,相比 TCP 累积确认在重传效率上有何优势?

请说明 QUIC 按 packet number space 做丢包恢复,相比 TCP 累积确认在重传效率上有何优势?

  • packet number space(包号空间)
  • 丢包恢复与确认
  • 相比 TCP 累积确认的优势

QUIC 把包号按 packet number space 划分(Initial、Handshake、0-RTT/1-RTT 各自独立的包号空间),每个包号单调递增且不重传(重传的包用新包号),并基于 ACK 范围(ACK ranges)精确报告哪些包号已收到。相比 TCP 累积确认(只反映连续收到的最高序列号),QUIC 的优势:1) 包号空间分离避免重传时包号歧义(TCP 重传复用序列号会造成 RTT 采样歧义),RTT 估计更准确;2) ACK 范围可精确表达多个丢失段的已收情况,发送端能精确重传缺失包,无需冗余重传;3) 新包号使每次重传独立计时,丢失检测更精确。因此 QUIC 的丢包恢复更高效、RTT 测量更准。

QUIC 用"独立包号空间 + 新包号重传 + ACK 范围"解决 TCP 累积确认的歧义与冗余重传,提升重传效率与 RTT 精度。

#
★★

46. QUIC 的建立,0-RTT 与 1-RTT 握手、连接迁移与队头阻塞消除如何实现?

请综合说明 QUIC 的建立过程(0-RTT 与 1-RTT 握手)、连接迁移与队头阻塞消除?

  • 1-RTT/0-RTT 握手
  • 连接迁移
  • 队头阻塞消除

QUIC 建立:首次连接用 1-RTT 握手(客户端 Initial 带 ClientHello,服务端回应,一个往返后建立连接并可用应用密钥);若客户端有缓存会话参数(PSK,如重复连接),用 0-RTT 握手,客户端在握手完成前就发应用数据,0 往返建立连接。连接迁移:QUIC 用连接 ID 标识连接而非 4 元组,网络切换(Wi-Fi 转蜂窝)时地址变化但连接 ID 不变,配合路径验证(PATH_CHALLENGE/RESPONSE)和无缝迁移,连接不断。队头阻塞消除:QUIC 把数据分成多个独立 stream,每个流独立排序,某流丢包只阻塞该流,其他流可立即交付,消除 TCP 单字节流上的跨流队头阻塞。三者共同构成 QUIC 的核心优势:快速建连、无缝迁移、低队头阻塞。

QUIC 的建连(0-RTT/1-RTT)、迁移(连接 ID)、队头阻塞消除(多流独立)是三大核心特性,分别解决建连延迟、移动性、多路复用阻塞。

#
★★

47. QUIC 的 spin bit 如何用于被动 RTT 测量,为什么默认关闭且不提供加密语义?

请说明 QUIC 的 spin bit 如何用于被动 RTT 测量,以及它为何默认关闭且不提供加密语义?

  • spin bit 与被动 RTT 测量
  • 默认关闭与隐私
  • 无加密语义

QUIC 的 spin bit(自旋位)是传输层的一个位,接入端(如中间网络设备、被动观察者)通过观察该位在连接上的翻转模式,可被动地估算连接的 RTT(客户端向服务端发送时翻转 spin bit,观察到翻转周期即可推断 RTT),无需主动参与。它默认关闭的原因:spin bit 会泄露连接的往返延迟信息(可观测性 vs 隐私),暴露连接行为可能被用于侧信道或流量分析,默认关闭以保护隐私;且它不提供加密语义——spin bit 本身不加密任何数据,只是一个可观测的标志位,不承载数据、不保证保密性,仅用于被动 RTT 测量。因此默认关闭,需要时由端点显式开启。

spin bit 用于被动 RTT 观测,但会泄露延迟信息,默认关闭以保护隐私;它只是标志位,无加密语义。

#
★★

48. HTTP/3 相对 HTTP/2 为何移除 server push,RFC 9218 的 Extensible Priorities 如何表达请求优先级?

请说明 HTTP/3 相对 HTTP/2 为何移除 server push,以及 RFC 9218 的 Extensible Priorities 如何表达请求优先级?

  • 移除 server push 的原因
  • RFC 9218 Extensible Priorities
  • 优先级表达

HTTP/3 移除 server push 的原因:HTTP/2 的 server push 实际使用率低、与缓存/多路复用交互复杂(推送的资源可能不需要、浪费带宽、管理复杂),且带来缓存污染与部署问题,故 HTTP/3 不再支持 server push(改为等其他机制,如预加载、cache 预取)。RFC 9218 的 Extensible Priorities 用优先级字段表达请求优先级:给每个请求分配 urgency(紧急度,0-7)和 incremental(是否增量传输)两个参数,替代 HTTP/2 的依赖树。客户端在请求头中携带 priority 字段(如 priority: u=1, i),服务端据此调度资源:urgency 决定请求的相对优先级,incremental 表示该请求是否可与其他请求增量交错传输。相比 HTTP/2 的依赖树,Extensible Priorities 更简单、可扩展、易实现。

server push 因低使用率与复杂性被移除。RFC 9218 用 urgency+incremental 简化优先级表达,替代 HTTP/2 的依赖树。

#
★★

49. TCP zero window 通告作为接收方背压?

请说明 TCP zero window 通告如何作为接收方背压机制?

  • zero window 通告
  • 接收方背压
  • zero window probe

TCP zero window 通告是接收方向发送方通告"接收窗口为 0"(rwnd=0),表示接收方缓冲区已满、暂时无法接收更多数据,从而要求发送方停止发送,形成接收方背压(backpressure)。当接收方应用处理速度跟不上接收速度时,接收缓冲区满,接收方在 ACK 中通告 rwnd=0,发送方就该停止发送,直到接收方恢复(通告非零窗口)。这是"接收方控制发送速率"的机制:发送方实际发送量受 min(cwnd, rwnd) 限制,rwnd=0 时即使网络畅通也暂停发送,防止接收方缓冲区溢出。为避免死锁(发送方等窗口、接收方等数据),发送方在窗口为 0 时会周期发送 zero window probe(探测窗口是否恢复),接收方若窗口恢复则通告非零窗口继续传输。

zero window 是接收方显式的背压信号(rwnd=0),暂停发送并靠 probe 恢复,实现"接收方按处理能力控制发送"。

#
★★

50. TCP slow start 与 congestion avoidance 在背压中的角色?

请说明 TCP slow start 与 congestion avoidance 在背压中的角色?

  • slow start 的角色
  • congestion avoidance 的角色
  • 背压与拥塞控制

slow start 与 congestion avoidance 是 TCP 拥塞控制的两个阶段,负责"网络背压"(根据网络容量调整发送速率):slow start 用指数增长快速探测可用带宽(窗口每 RTT 翻倍),快速到达接近容量的水平;congestion avoidance 用线性增长(每 RTT 加 1)在接近容量时缓慢试探,避免过度发送导致拥塞。它们是"网络侧背压"的体现:发送速率由网络拥塞(通过丢包/ACK/ECN 反馈)驱动,而不是由接收方驱动。与接收方背压(rwnd)不同,slow start/congestion avoidance 处理的是"网络容量"——发送端通过它们适应网络带宽,避免拥塞崩溃。实际发送量 = min(拥塞窗口, 接收窗口),slow start/congestion avoidance 决定拥塞窗口(网络背压),接收窗口决定接收方背压。

slow start/congestion avoidance 是"网络背压"(拥塞窗口),与接收方背压(rwnd)互补,共同决定发送速率。它们防止网络拥塞。

#
★★

51. Linux sysctl net.ipv4.tcp_abc 在 explicit backpressure 中的角色?

请说明 Linux sysctl net.ipv4.tcp_abc 在显式背压(explicit backpressure)中的角色?

  • tcp_abc 参数
  • ABC(Appropriate Byte Counting)
  • 显式背压

net.ipv4.tcp_abc 控制 Linux TCP 的 ABC(Appropriate Byte Counting,适当字节计数):tcp_abc=1 时,TCP 在拥塞避免阶段按"确认的字节数"(而非"确认的报文段数")来增长拥塞窗口,更准确地反映实际吞吐;tcp_abc=2 还允许在从慢启动退出时保持部分字节计数。在显式背压(explicit backpressure)中的角色:ABC 让窗口增长与实际传输字节挂钩,避免"窗口增长与实际数据不匹配"带来的不公平或过度增长,使 TCP 在基于 ACK 的背压(拥塞反馈)下更精确地调整发送速率,从而在 ECN/ACK 驱动的显式背压场景下更平滑、更公平地响应。它属于"窗口增长策略"的调节,使拥塞控制更贴近真实字节吞吐。

tcp_abc 用字节计数替代段计数增长窗口,使拥塞控制更精确,在显式背压(ACK/ECN 反馈)下更平滑公平。

#
★★

52. TCP selective acknowledgement(SACK)下拥塞避免的窗口调整?

请说明 TCP 在启用 SACK 时拥塞避免阶段的窗口调整方式?

  • SACK 与损失检测
  • 拥塞避免窗口调整
  • 部分丢失的处理

启用 SACK 后,TCP 能精确知道哪些段丢失、哪些段已收到,拥塞避免阶段的窗口调整更智能:遇到丢包时,即使只丢失部分段(非全部),也会触发拥塞避免操作——把 ssthresh 下调(通常为当前窗口一半),并(若用快速重传/快速恢复)把拥塞窗口临时设为 ssthresh,然后进入拥塞避免的线性增长。SACK 的窗口调整特点:它允许在拥塞避免期间"部分恢复"——已 SACK 确认的段不占用窗口,发送端可重传缺失段并继续发送新段,窗口管理更精细,避免不必要的整体窗口骤降。与无 SACK 相比,SACK 下的窗口调整更能反映真实丢失情况,减少不必要的重传与窗口收缩,提高吞吐。

SACK 让"丢的是哪些段"精确可见,拥塞避免窗口调整据此重传缺失段并继续发送,避免整体窗口不必要的骤降。

#
★★

53. 推导 TCP 接收窗口(rwnd)满时 zero window 探测的最小间隔?

请推导 TCP 接收窗口(rwnd)满时 zero window 探测的最小间隔?

  • zero window probe 间隔
  • 指数退避
  • 最小间隔

TCP 在接收窗口为 0(zero window)时,发送方会周期发送 zero window probe(1 字节的探测包)以探测接收方窗口是否恢复。probe 的间隔采用指数退避(exponential backoff):初始间隔为一个 RTO(重传超时)或 RTO 的某个倍,之后每次加倍(RTO、2×RTO、4×RTO...),直到达到最大(如 60 秒或 120 秒)。因此最小间隔是初始的 RTO(通常基于估算的 RTT 计算,如 RTO = SRTT + max(G, 4×RTTVAR),并落在 [1s, 60s] 范围内)。也就是说,最小探测间隔至少为初始 RTO(约 1 秒量级),而不是 0 或很小的固定值,因为过频探测会浪费带宽。实际最小间隔即初始 RTO,正是"指数退避"的起点。

zero window probe 用指数退避,最小间隔为初始 RTO(约 1 秒),避免过频探测浪费带宽,同时保证窗口恢复能被及时感知。

#

54. RTO、快速重传与 RACK/TLP 的触发依据有何差异,为什么尾部丢包更难发现?

请说明 RTO、快速重传与 RACK/TLP 的触发依据有何差异,以及为何尾部丢包更难发现?

  • RTO(超时)与快速重传(重复 ACK)
  • TLP(尾部丢包探测)与 RACK
  • 尾部丢包难发现的原因

RTO(重传超时)基于超时:在某包发出后 RTO 内未收到 ACK 即重传,用于检测完全无反馈的丢包。快速重传基于重复 ACK:收到 3 个重复 ACK(DUP ACK)即认为是丢包并重传,用于检测"有后续数据被确认但某段缺失"的丢包。TLP(Tail Loss Probe)用于尾部丢包:当数据传输接近结束(无后续数据可触发重复 ACK)时,主动发送一个探测包,若未确认则触发快速重传,弥补"尾部丢包无重复 ACK 可触发"的盲区。RACK(Recent ACK)基于最近 ACK 的时间与序列号,用"最近确认的包之后的包是否在合理时间内被确认"来判定丢包,比重复 ACK 更准。尾部丢包更难发现的原因:尾部丢包后没有后续数据包,接收端不会发重复 ACK(无法触发快速重传),只能等 RTO 超时(通常较长),导致恢复慢;TLP/RACK 正是针对此的改进。

RTO 靠超时、快速重传靠重复 ACK、TLP 靠主动探测、RACK 靠最近 ACK 时间。尾部丢包无后续包触发重复 ACK,只能等 RTO,故难发现,TLP/RACK 弥补。

#

55. 当 RTT 估算抖动较大时 PTO(Probe Timeout)为何被引入作为 RTO 的备份,与传统的 RTO 重传算法有何区别?

请说明当 RTT 估算抖动较大时,PTO(Probe Timeout)为何被引入作为 RTO 的备份,以及它与传统 RTO 重传算法有何区别?

  • PTO 与 RTO 的关系
  • RTT 抖动时的探测
  • PTO 与 RTO 的区别

QUIC 中 RTO 由 RTT 估算(SRTT + 4×RTTVAR)决定,当 RTT 估算抖动较大时,RTO 可能设置过短或过长:过短导致过早重传(假丢包),过长导致恢复慢。PTO(Probe Timeout)被引入作为 RTO 的备份:它基于更保守的 RTT 估算(以估算 RTT 的较大倍数,如 1.5×RTT 或基于 smoothed RTT 的高值),用于在 RTT 抖动时提供一个更稳健的"探测超时",触发发送探测包(ack-eliciting probe)以确认是否丢包,而不是盲目重传。区别:传统 RTO 是"认定丢包后重传全部数据",PTO 是"先发探测包确认,再决定是否重传",更保守、避免假丢包;PTO 用指数退避,且结合 QUIC 的尾部探测(TLP)语义,在 RTT 抖动时更稳健。PTO 主要替代 RTO 的"探测"角色,QUIC 用 PTO 统一处理丢包探测。

RTO 依赖 RTT 估算,抖动时不可靠;PTO 用保守估算 + 探测包确认,作为 RTO 的备份,避免抖动导致的假丢包与重传。

#

56. MSG_PEEK、MSG_DONTWAIT、MSG_NOSIGNAL 各自改变 recv/send 的什么行为?为何常需屏蔽 SIGPIPE?

请说明 MSG_PEEK、MSG_DONTWAIT、MSG_NOSIGNAL 各自改变 recv/send 的什么行为,以及为何常需屏蔽 SIGPIPE?

  • MSG_PEEK(窥探不消费)
  • MSG_DONTWAIT(非阻塞)
  • MSG_NOSIGNAL(抑制 SIGPIPE)

MSG_PEEK 用于 recv:查看接收缓冲区中的数据但"不消费"(不取出),数据仍可被后续 recv 读取,用于预读取判断。MSG_DONTWAIT 让 recv/send 以非阻塞方式执行(即使 fd 未设 O_NONBLOCK),无数据/缓冲区满时立即返回 EAGAIN,不阻塞。MSG_NOSIGNAL 用于 send:当向已关闭的对端发送(触发 EPIPE)时,不产生 SIGPIPE 信号(默认 SIGPIPE 会终止进程),而是返回 EPIPE 错误。为何屏蔽 SIGPIPE:向对端已关闭的 socket 发送数据会触发 SIGPIPE,默认 SIGPIPE 会直接终止进程,导致服务意外崩溃;用 MSG_NOSIGNAL(或忽略 SIGPIPE 信号)让 send 返回错误码而非杀死进程,便于应用处理写失败。

三个标志分别改"窥探不消费""非阻塞""抑制信号"。屏蔽 SIGPIPE 避免因向关闭连接写数据导致进程被信号杀死。

#

57. listen 的 backlog 涉及 SYN queue 与 accept queue,accept queue 溢出时客户端会观察到什么现象?

请说明 listen 的 backlog 涉及 SYN queue 与 accept queue,以及 accept queue 溢出时客户端会观察到什么现象?

  • backlog 与 SYN queue / accept queue
  • accept queue 溢出
  • 客户端现象

listen 的 backlog 决定 accept queue(已完成三次握手、等待 accept 的连接队列)的大小,同时相关 SYN queue(尚未完成握手的半连接队列)由内核参数控制。三次握手完成后,连接进入 accept queue,等待应用 accept()。若 accept queue 溢出(应用处理慢、队列满),新完成握手的连接无法进入队列,内核会丢弃或发 RST(取决于是否启用 tcp_abort_on_overflow)。客户端现象:连接可以建立(SYN 握手正常)但随后被 RST 或超时,表现为"连接建立后立即断开""连接超时""连接被重置",高并发下大量请求失败;若 tcp_abort_on_overflow=1,内核发 RST 拒绝对应连接,客户端看到 connection reset。

accept queue 溢出是"应用 accept 慢"导致,客户端表现为建连后断开/超时/RST。监控队列溢出与 backlog/accept 速率是关键。

#

58. QUIC 中 ack-eliciting 报文与 PTO 退避,为什么纯 ACK 不触发重传计时器,PTO 指数退避的作用是什么?

请说明 QUIC 中 ack-eliciting 报文与 PTO 退避,为何纯 ACK 不触发重传计时器,以及 PTO 指数退避的作用?

  • ack-eliciting 报文
  • 纯 ACK 不触发重传
  • PTO 指数退避

QUIC 中,ack-eliciting 报文(需要对端确认的报文,如含数据的包、PING 等)会触发重传/探测计时器(PTO);纯 ACK 报文(不含数据、不要求对端确认)不触发重传计时器,因为 ACK 只是确认信息,丢失后对端会重新确认或由后续 ACK 覆盖,无需重传,避免无意义的重传。PTO(Probe Timeout)指数退避的作用:当探测超时后,PTO 间隔按指数退避(如 1×、2×、4×...)增大,避免在持续丢包/网络故障时高频探测造成网络负担与放大;指数退避让探测频率随持续失败而降低,同时保留定期探测以感知恢复。这与 TCP 的 RTO 退避类似,是"失败后逐步降低重试频率"的稳健策略。

ack-eliciting 才触发重传计时器,纯 ACK 无确认需求故不触发。PTO 指数退避在持续故障时降低探测频率,避免放大与负担。

#

59. QPACK 相对 HPACK 为何改用编码器/解码器流显式同步动态表,而不是在请求流中隐式更新?

请说明 QPACK 相对 HPACK 为何改用编码器/解码器流显式同步动态表,而非在请求流中隐式更新?

  • HPACK 的隐式动态表更新
  • QPACK 的编码器/解码器流
  • 队头阻塞与流模型

HPACK(HTTP/2)的动态表更新是"隐式"的:头部字段的增量更新随请求/响应流内的数据一起传递,依赖流内顺序,一旦某个流丢包/乱序,动态表同步会阻塞该流,且与 HTTP/2 的流模型耦合。QPACK(HTTP/3)改用编码器流(Encoder Stream)和解码器流(Decoder Stream)显式同步动态表:编码器流单向传输封装指令(新增/更新动态表条目),解码器流反向确认(如 ACK 动态表条目、流取消),动态表更新与请求流解耦,独立于请求流传输。这样即使某个请求流丢包,动态表状态仍可从编码器流独立同步,不阻塞其他流,避免队头阻塞;且显式同步让编码器/解码器状态一致,与 QUIC 的多流模型匹配。因此 QPACK 用显式流同步解决 HPACK 隐式更新的阻塞问题。

HPACK 隐式更新与流耦合、易阻塞;QPACK 用独立编码器/解码器流显式同步动态表,与请求流解耦,避免队头阻塞。

#

60. HTTP/3 的帧与流,与 HTTP/2 的差异及服务端部署如何?

请说明 HTTP/3 的帧与流与 HTTP/2 的差异,以及服务端部署的考虑?

  • HTTP/3 与 HTTP/2 的帧/流差异
  • 流类型与队头阻塞
  • 服务端部署

HTTP/3 与 HTTP/2 的帧与流差异:HTTP/2 帧在 TCP 上多路复用,所有流共享 TCP 字节流,存在跨流队头阻塞;HTTP/3 帧在 QUIC 流上传输,每个流独立排序,消除跨流队头阻塞。HTTP/3 引入新的流类型:控制流(SETTINGS、GOAWAY 等)、QPACK 编码器/解码器流、请求流、推送流等,帧类型也与 HTTP/2 不同(如移除了某些帧、新增 PUSH 相关帧)。HTTP/3 的帧承载在 QUIC 流上,流与帧的映射更灵活。服务端部署考虑:需支持 QUIC(UDP 443)与 HTTP/3 的 ALPN(h3),负载均衡器/反向代理需识别 QUIC 连接 ID 做会话保持与分片,需处理 UDP 离线、MTU、防火墙放行 UDP 443,提供 HTTP/2 回退以保证兼容性,并监控 QUIC 性能与迁移。

HTTP/3 用 QUIC 流承载帧消除跨流 HOL,并引入控制流/QPACK 流等新类型;部署需支持 UDP 443、CID 路由、HTTP/2 回退。

#

61. QUIC 的连接迁移,连接 ID 与网络切换如何配合?

请说明 QUIC 连接迁移中连接 ID 与网络切换的关系?

  • 连接 ID 与迁移
  • 网络切换流程
  • 保持连接

QUIC 连接迁移依赖连接 ID:连接由连接 ID(CID)标识而非 4 元组,网络切换(如 Wi-Fi 转蜂窝)时源 IP/端口变化,但连接 ID 不变,对端仍能识别同一连接。迁移流程:客户端检测到网络切换后,用新源地址发送数据,携带原连接 ID(或通过 NEW_CONNECTION_ID 帧获取的新 CID),对端收到后更新地址映射,并通过 PATH_CHALLENGE/PATH_RESPONSE 验证新路径,验证通过后正式切换主路径,连接保持不断。连接 ID 让"连接身份"与"当前地址"解耦,因此网络切换(地址变化)不影响连接身份,实现无缝迁移。同时新 CID 的更换可防止流量被关联/追踪。

连接 ID 是迁移的基础,它解耦连接身份与地址;网络切换改地址、保 CID,配合路径验证实现无缝迁移。

#

62. QUIC 的 0-RTT 重放攻击与防护?

请说明 QUIC 0-RTT 的重放攻击机制与防护措施?

  • 0-RTT 重放攻击
  • 防护措施
  • 幂等限制

QUIC 0-RTT 重放攻击:0-RTT 允许客户端在握手完成前用缓存的 PSK 派生 0-RTT 密钥发送数据,0-RTT 密钥无防重放机制,攻击者在网络上截获 0-RTT 包后可原样重放,服务端无法区分原始与重放,导致重复处理(如重复下单、重复操作)。防护措施:1) 服务端只接受 0-RTT 中的幂等/安全请求(如只读、幂等操作),拒绝非幂等操作(写、支付)在 0-RTT 处理;2) 用 anti-replay 机制(如记录已处理的 0-RTT 包、nonce/时间戳去重),检测重放并丢弃;3) 限制 0-RTT 的请求类型与范围,敏感操作强制用 1-RTT;4) 设置 0-RTT 数据有效期(重放窗口),过期即失效。客户端也应避免在 0-RTT 中发送会改变状态的数据。

0-RTT 重放源于 PSK 派生密钥无防重放,防护靠"幂等限制 + 去重 + 重放窗口过期 + 限制 0-RTT 范围"。

#

63. QUIC 的流量控制与流优先级,HTTP/3 的依赖树如何构建?

请说明 QUIC 的流量控制与流优先级,以及 HTTP/3 是否仍有依赖树?

  • QUIC 流控(流级+连接级)
  • 流优先级
  • HTTP/3 与依赖树

QUIC 的流量控制分层:流级流量控制(每个 stream 有 MAX_STREAM_DATA 窗口)限制单流数据量,连接级流量控制(MAX_DATA 窗口)限制所有流总量,发送端取两者较小值,避免单流耗尽资源也防止多流超限。流优先级:QUIC 本身不定义流优先级,由上层(HTTP/3)通过 RFC 9218 的 Extensible Priorities 表达(urgency 0-7 + incremental),HTTP/3 据此调度请求流。HTTP/3 是否仍有依赖树:HTTP/3 移除了 HTTP/2 的依赖树(dependency tree)优先级模型,改用 RFC 9218 的 Extensible Priorities(urgency + incremental 两个字段),更简单、可扩展、易实现,不再需要复杂的依赖树结构。因此 HTTP/3 不再使用依赖树,而是用简洁的优先级字段。

QUIC 流控分流级+连接级,流优先级由 HTTP/3 的 RFC 9218 表达(urgency+incremental),HTTP/3 已移除 HTTP/2 的依赖树。

#

64. QUIC Key Update(TLS 1.3 密钥更新)如何在不中断连接的情况下轮换流量密钥,旧密钥的重放窗口如何处理?

请说明 QUIC Key Update(TLS 1.3 密钥更新)如何在不中断连接的情况下轮换流量密钥,以及旧密钥的重放窗口如何处理?

  • TLS 1.3 密钥更新(Key Update)
  • 平滑轮换流量密钥
  • 旧密钥重放窗口

QUIC 支持 TLS 1.3 的 Key Update,在不中断连接的情况下轮换流量密钥:端点用 HKDF 从当前密钥派生新的应用密钥(每更新一次密钥派生一路),更新后发送新密钥的包,对端检测到新密钥(通过 KEY_PHASE 位或密钥阶段)后同步切换,双方在密钥过渡期可同时接受新旧密钥的包(容忍窗口),保证平滑切换、不中断连接。旧密钥的重放窗口处理:密钥更新后,旧密钥虽不再用于新数据,但在过渡期内仍可能用于接收"旧密钥帧"(在途包),QUIC 保留一个有限的旧密钥接收窗口(如短暂的过渡期),过期的旧密钥包被丢弃;重放防护上,Key Update 后旧密钥的包若在窗口外被重放会被拒绝(因为密钥已前进、包号空间/密钥阶段不匹配),且 QUIC 用包号单调性检测重放。旧密钥的重放窗口被限制在过渡期,避免旧密钥长期可用。

Key Update 用 HKDF 派生新密钥、平滑切换,旧密钥保留短暂重放窗口处理在途包,过期后旧密钥包被丢弃,防止重放。