shadrowroket
同一域名同时返回 IPv4 和 IPv6 时,应用为什么不一定总选同一条连接
双栈应用会根据 DNS 回答、地址排序和连接尝试时序选择 IPv4 或 IPv6。本文依据 RFC 8305 与 Apple 技术文档,说明当次赢家、地址族健康和 DNS64/NAT64 环境之间的边界。
同一个域名同时有 IPv4 和 IPv6 地址时,一台设备今天可能通过 IPv6 建立连接,换到手机网络后又显示 IPv4。只看最终连接,很容易得出“IPv6 已坏”或“系统固定偏爱 IPv4”的结论。实际上,现代双栈应用常会把解析、地址排序和连接尝试组合成一次快速选择。胜出的协议只说明这次选择先完成,不能单独证明另一协议不可达。
IETF 的 RFC 8305 把这类方法称为 Happy Eyeballs Version 2。它的目标是减少双栈网络中用户可见的等待,同时仍倾向使用 IPv6。算法不是同时无节制地发送所有连接,也不是永久记住一条线路。它先取得候选地址、形成顺序,再以受控间隔交错尝试;其中一条连接成功后,其他尝试被取消。
DNS 回答决定候选,不直接决定赢家
双栈客户端访问主机名时,会查询 AAAA 与 A 记录。RFC 8305 建议两类查询尽快相继发出,并优先发 AAAA。客户端不应等待两类回答全部返回后才开始连接,因为某一类回答延迟会拖慢已经可用的另一类。
若 AAAA 正面回答先到,客户端可以立即开始 IPv6 尝试。若 A 回答先到,示例算法会短暂等待 AAAA,以保留 IPv6 优先的机会。RFC 推荐的解析等待值是五十毫秒,但这只是可调默认值,不是判断网络是否正常的固定阈值。
DNS 返回多个地址时,它们都是候选,不代表每个地址都在同一机房、同一链路或具有相同延迟。域名背后的负载分配、缓存、记录顺序和回答到达时间都可能变化。两次连接选择不同地址,不足以说明系统设置已改变。
地址排序发生在连接竞速之前
客户端取得地址后,要先按目的地址选择规则排序。RFC 8305 还允许利用同一网络接口上的历史往返时间和过去成功地址,调整候选顺序。设备换到另一张网络后,这些历史数据不应继续沿用,因为新接口的路径条件已经不同。
排序后,客户端会交错两个地址族。若列表第一项是 IPv6,通常把第一个 IPv4 候选移到靠前位置,避免 IPv6 列表很长且全部受阻时逐个等待。实现也可以更偏向某一族,连续尝试多个同族地址,但这属于实现参数。
因此,最终连接族由多个因素共同决定:DNS 回答到达顺序、目的地址排序、历史数据、接口变化和实际握手时间。把它简化为“系统先 IPv6,失败才 IPv4”会漏掉异步解析与交错列表,也无法解释某次 A 回答更早时发生的行为。
连接尝试不是毫无间隔的并发
Happy Eyeballs 会启动第一个地址的连接,再经过短间隔启动后续候选。先前尝试不会因为后一个开始就立即停止,所以一段时间内可能存在并行握手。第一条成功连接一般在握手完成时胜出,其他未成功尝试随后取消。
RFC 的示例默认连接尝试间隔是二百五十毫秒,建议最小值为一百毫秒,并规定不得低于十毫秒。它还给出两秒的建议上限。标准明确说明这些数值来自生产设备的经验测量,网络性质会改变,实现者可以采用不同值。
看到两款应用选择不同地址族,不应据此断言其中一款违反标准。它们可能使用不同历史数据、排序政策与延迟值,也可能依赖操作系统网络库。除非应用公开实现,外部观察只能记录连接结果和时间,不能反推出完整算法参数。
一次胜出不能证明另一族失败
假设 IPv6 握手用了八十毫秒,IPv4 在稍后启动后用了三十毫秒。若两者完成时序接近,具体赢家可能受调度和网络波动影响。IPv4 胜出不等于 IPv6 不可用,只说明当次连接建立先完成。反过来也一样。
要判断某个地址族是否真的故障,需要独立观测。应分别记录 A 与 AAAA 回答、每个候选地址的连接结果、超时阶段和网络接口。连续多次只记录最终赢家,会把被取消的尝试误当作失败,也看不到根本没有被尝试的候选。
RFC 还警告 Happy Eyeballs 可能隐藏运营问题。若某网络的 IPv6 长期配置错误,而 IPv4 总能快速接替,使用者仍会感觉页面正常。运营方需要独立监测两个地址族,不能把应用连接成功率当作双栈健康证明。
握手成功也不等于应用正常
RFC 8305 处理的是初始连接阶段。路径最大传输单元问题可能在握手后才出现,TLS 或 HTTP 服务也可能只在部分后端异常。某地址完成 TCP 握手,不保证页面、登录、下载或长连接都能正常工作。
这为记录增加了第二层。第一层是 DNS 和传输连接:返回哪些地址、尝试何时开始、谁完成握手。第二层是应用结果:TLS 是否成功、HTTP 状态如何、主体是否完整、长连接是否维持。两层不能互相代替。
如果连接建立很快但页面后续停住,应继续查看应用阶段,不要反复比较 IPv4 与 IPv6 赢家。若握手本身持续超时,再回到地址族与路径观测。把故障阶段分开,可以避免把所有问题都归到 Happy Eyeballs。
DNS 变化和连接复用会改变观察窗口
连接建立期间,DNS 答案也可能增加、移除或因 TTL 到期而变化。RFC 8305 规定,已经启动的地址尝试不会仅因记录被移除就立刻取消;尚未开始的候选则应从列表删除。新地址可以按排序规则加入尚未尝试的队列。这说明抓到一次 DNS 回答,并不一定等于连接引擎当时掌握的完整候选历史。
应用还可能复用既有连接,所以同一页面的相邻请求未必再次执行地址竞速。页面上的第二个请求没有新建传输连接时,就不存在新的地址竞速。若只比较页面请求时间,却不区分新连接和复用连接,可能把同一条既有 IPv6 连接误写成系统再次选择 IPv6。测试记录应标出是否创建新连接,而不是强行关闭所有复用功能。
RFC 的安全章节也提醒,应用不能依赖主机名永远映射到同一地址来保证安全属性。DNS 结果可以变化,后续连接也可能选择不同 IP。身份验证应依赖适合的 TLS 和应用层机制,不应把“仍连接同一 IP”当作唯一信任条件。
网络变化会重置可用经验
手机从家庭 Wi-Fi 切到移动网络,路由、DNS 服务器、地址前缀和 NAT 条件都可能改变。RFC 要求历史地址使用情况不得跨不同网络接口沿用,并建议设备接入新网络时清除相关数据。旧网络中的快速地址不代表新网络仍然快速。
即使设备与域名相同,上午和晚间的 DNS 回答、负载分配和路径拥塞也可能不同。比较时要记录网络类型、接口、时间、A 与 AAAA 集合,以及最终应用结果。只有条件相近的样本才适合放在同一组中。
这也解释了为什么“昨天总是 IPv6,今天总是 IPv4”不能直接归因于系统升级。需要核对 DNS 回答是否变化、设备是否换网、目标地址是否相同,以及应用是否复用了既有连接。没有这些信息,协议标签只是结果,不是原因。
IPv6-only 网络中的地址可能是合成的
在 DNS64/NAT64 网络中,客户端只使用 IPv6,也能访问只提供 IPv4 的服务器。DNS64 在没有原生 AAAA 时取得 A 记录并合成 IPv6 地址,NAT64 网关再执行协议转换。表面看到 IPv6 目的地址,不一定表示目标服务器原生提供 IPv6。
Apple 的开发文档建议客户端使用主机名和高层网络框架,避免硬编码 IPv4 地址。用主机名连接时,系统可以在 DNS64/NAT64 环境中完成必要处理。若应用自行拼接固定前缀,可能只在部分网络碰巧有效,因为 NAT64 前缀并不总是公认前缀。
RFC 8305 也限定了本地合成功能的启用条件。它应在主机检测到可路由 IPv6、没有可路由 IPv4且存在 DNS 解析器的环境中使用,而不是在所有网络强制开启。把双栈连接和 IPv6-only 翻译环境混为一谈,会错误解释地址来源。
本地测试环境有明确限制
Apple 提供的本地 DNS64/NAT64 测试网络会始终生成合成 IPv6 地址,并在广域网一侧通过 IPv4 传输。它适合发现应用中的 IPv4 字面量和地址族假设,却不是运营商网络的完全复制品。
如果服务器公布 AAAA 却没有真正处理 IPv6,本地测试可能仍显示正常,因为流量最终走的是服务器 IPv4 路径。到了允许原生 IPv6 的真实网络,应用才会失败。Apple 因此要求另外核对 AAAA、服务监听与原生 IPv6 响应。
这构成一个受控比较:本地 DNS64/NAT64 测试验证翻译兼容性,原生 IPv6 网络验证服务器真实双栈能力,普通双栈网络观察地址竞速。三种结果服务不同问题,不能用其中一种替代全部环境。
怎样留下可复核的连接记录
一份记录至少包含主机名、查询时间、A 与 AAAA 回答、网络接口、候选地址、连接开始与结束时间,以及应用阶段结果。若工具只能显示最终连接地址,要明确写“其他候选未知”,不要把未知尝试补成超时。
比较两个样本时,先核对地址集合是否相同。集合不同,选择变化可能来自 DNS。集合相同,再看排序与完成时间。传输赢家相同而应用结果不同,则需要查看 TLS、HTTP 或后端状态。这个顺序能把观察放回实际阶段。
对普通使用者而言,不需要修改系统地址偏好来完成判断。保留当前配置,在不同网络和时间重复同一公开请求即可。涉及企业或学校网络时,应遵守管理政策,不用关闭 IPv6 或绕过 DNS 规则制造对照。
结论边界
同一域名今天走 IPv6、明天走 IPv4,可以是正常的双栈选择结果。Happy Eyeballs 同时考虑异步 DNS、地址排序和错开的连接尝试,目标是减少等待,不是为每次访问固定协议。胜出地址只说明当次连接先建立。
它也可能让另一地址族的长期故障不易被使用者察觉,所以双栈健康必须独立监测。连接握手成功后,应用层仍可能失败;DNS64/NAT64 看到的 IPv6 地址也可能是为 IPv4 目标合成的。把解析、传输、应用和翻译环境分开记录,才能判断变化发生在哪一层,而不是从一个协议标签推断整条线路。
资料来源
- IETF:《RFC 8305 Happy Eyeballs Version 2》,发布或更新于 2017-12-01
- Apple Developer:《Supporting IPv6 DNS64/NAT64 Networks》,发布或更新于 2017-03-27