Happy Eyeballs 让双栈客户端错开尝试 IPv6 和 IPv4,有一条连接成功就取消其余尝试。截至 2026 年 10 月,RFC 8305 建议的连接尝试间隔是 250 毫秒,各实现可以不同。VPN 只接管其中一条栈时,页面走哪一侧,取决于先发出的那次尝试有没有在间隔内连上。
它在决定哪一次连接留下
一个可独立引用的定义句:Happy Eyeballs(RFC 8305,取代 RFC 6555)是双栈主机建立连接时错开尝试多个目的地址的方法,用来减少某一地址族损坏时用户看到的等待,同时仍优先 IPv6。
规范把过程分成四步:异步发出 AAAA 和 A 查询,用目的地址选择(RFC 6724)排序,按间隔逐个发起连接,某一条成功后取消尚未成功的其他尝试。它比较的是到这个 IP 的传输层握手能不能完成,不是这条流量有没有进入 VPN。
一个可独立引用的结论句:竞速选出的是目的地址;这个地址走隧道还是走物理网卡,由路由表决定。
两次等待各等什么
据 RFC 8305,有两个容易混在一起的间隔:
| 名称 | 规范中的建议值 | 在等什么 |
|---|---|---|
| 解析延迟(Resolution Delay) | 50 毫秒 | A 记录先到时,短暂再等 AAAA,以便仍能优先 IPv6 |
| 连接尝试延迟(Connection Attempt Delay) | 250 毫秒 | 上一次连接尝试发出之后,隔多久再尝试列表中的下一个地址 |
AAAA 先到并且至少有一条可用记录时,IPv6 连接应立刻开始,不必为了等 A 再停 50 毫秒。A 因乱序先到时,才使用解析延迟。
连接尝试不应同时发出。规范给出的下限是:下一次尝试不得早于上一次之后 10 毫秒,建议的最小间隔是 100 毫秒;建议的上限是 2 秒。有历史往返时间时,间隔可以改成接近上一次 TCP 第一次重传 SYN 的时刻,而不是永远固定 250 毫秒。这些数字是建议默认值。RFC 写明实现可以采用不同数值,并预期随着 IPv6 故障变少而把等待加长。
排序时,客户端还应该把两个地址族交错开。若排序后的第一个地址是 IPv6,就把第一个 IPv4 提前到第二位,避免在一长串损坏的 IPv6 地址上耗完整段延迟。规范建议连续尝试同一地址族的个数(First Address Family Count)为 1,也可以用 2 来更偏向某一族。
因此,「谁先通就用谁」只描述已经发出的那些尝试。下一条尝试还没到点时,没有第二名可以比较。站内其他文章若把整个算法收成这一句,指的是竞速的结果,不是两条连接同时起跑。
只接管一条栈时会发生什么
路由和竞速是两层。IPv6 泄漏回答的是数据包从哪张网卡离开;本文回答的是应用先向哪个目的地址握手。
| 路由现状 | A 与 AAAA | 更可能留下的连接 |
|---|---|---|
| 两条默认路由都进隧道 | 都有 | 间隔内连上的那条;排序上通常先试 IPv6 |
| 只有 IPv4 进隧道,IPv6 仍走物理网卡 | 都有 | IPv6 若在间隔内握手成功,页面走真实 v6;失败才轮到隧道里的 IPv4 |
| IPv6 默认路由指向隧道,但隧道并不转发 v6 | 都有 | 先发出的 v6 尝试会空等,大约一个间隔后 IPv4 仍可连上 |
| 只有 A,或解析只给出一个地址族 | 缺一侧 | 没有跨族竞速,只用返回的那一族 |
最后一行在使用虚假 IP、且只合成 A 记录时会出现。应用看不到 AAAA,就不会去试 IPv6。占位地址和真实地址混在一次解析里时,竞速会让结果更难读,见 Fake-IP 是什么。
一个可独立引用的结论句:IPv6 能在连接尝试延迟内完成握手时,客户端可以不再尝试 IPv4。IPv4 已经进入隧道,并不能单独保证这次页面走的是隧道。
历史往返时间可以插入地址排序,但 RFC 8305 要求这些记录不得跨网卡共用,并建议设备更换所附着的网络时清掉。某一次实现是否把「VPN 接口刚起来」当成更换网络,要看该系统,不能从规范直接推出。
握手成功之后它就不再介入
RFC 8305 第 9 节写明:这个算法处理的是传输层握手阶段的失败。路径 MTU 问题发生在握手之后,TCP 已经完成三次握手,竞速不会回头再选另一条地址。小请求正常、大页面卡住时,仍要按 MTU 不匹配 来看。
同一域名的多个 IP 也可能指向不同的应用服务器。传输层连上,不表示 TLS 证书名称或 HTTP 服务在那个地址上可用。
它还会把持续损坏的地址族藏起来。若 IPv6 每次都失败、IPv4 总是在一个间隔之后成功,用户多半只觉得网站慢了不到一秒,网络侧不容易从「页面最终打开了」判断 v6 是否黑洞。规范把这视为已知限制,并建议用别的手段观察。
和分流、企业内网名字
分流可以让一部分前缀留在隧道外。VPN 分流改变的是命中前缀的下一跳。Happy Eyeballs 仍会按返回的地址去连;被留在隧道外的那个地址族如果先连上,这次会话就不进隧道。
RFC 8305 第 7.3 节还描述了一种企业 VPN:内网解析器只返回一个外部 IPv4 地址,底层网络却只有 IPv6、靠 NAT64 访问外部地址。客户端拿到 A 记录,但没有到该 IPv4 的路由。规范讨论的是这种错位下客户端如何看待地址。本文只说明「解析结果和可用路由不属于同一地址族」会出现,不提供合成地址或改写解析的步骤。
现象对照
| 现象 | 更可能先看什么 |
|---|---|
| 检测页有时是隧道 IPv4,有时是真实 IPv6 | 两条栈路由不一致,竞速选中了直连的 v6 |
| 每个新连接都慢大约几百毫秒,之后正常 | 排序靠前的地址族握手失败,等到连接尝试延迟才换族 |
| 只有大资源失败,首包已经发出 | 握手后的 MTU,竞速已经结束 |
| 只有 A 记录的名字完全不走 IPv6 | 没有 AAAA 就没有跨族竞速 |
| 同一网站刚才走 v6、切换网络后走 v4 | 历史往返时间可能已清掉,或新网络只剩一个地址族 |
核对清单
- 先看答案里有哪些地址:A 和 AAAA 是否都在,还是只有一族;
- 再看实际连上的是哪一个:地址族以这次会话为准,不要只看隧道图标;
- 对照该地址的路由:
0.0.0.0/0与::/0是否都指向隧道;只写了 IPv4 时见 WireGuard AllowedIPs; - 把间隔当成建议值:50 毫秒和 250 毫秒来自 RFC 8305 的建议,某个浏览器可以更长或更短;
- 握手之后另算:大包失败、证书名称不对,都发生在竞速结束以后。