鲜为人知的 Happy Eyeballs

Happy Eyeballs 是什么?

Happy Eyeballs(也称 Fast Fallback)是一套 在双栈网络(同时有 IPv6 与 IPv4)中尽量缩短首包延迟、改善用户体验的连接竞速算法

RFC 6555 (2012) 首次提出;
RFC 8305 (2018) 将其升级为 Happy Eyeballs v2 并取代 6555,细化了 DNS 解析、连接并发与计时规则。(datatracker.ietf.org, datatracker.ietf.org)


1 背景与动机

现实网络里常出现 “IPv6 通了但质量差 / IPv6 被运营商屏蔽” 等问题。
传统客户端按 IPv6→IPv4 顺序尝试连接时,如果 IPv6 不可达,会先卡 10 ~ 30 s 超时,再回退到 IPv4 → 明显拖慢页面或 API 首开时间。
为了让 双栈用户 ≈ IPv4 单栈用户 的速度体验不受 IPv6 瑕疵影响,就需要一种智能、快速的并行尝试策略——这就是 Happy Eyeballs。


2 核心思想(v2 摘要)
步骤 说明
① 收集地址 对目标主机先做 AAAA+A 查询 → 获得 IPv6 与 IPv4 列表。
② 交错排序 IPv6, IPv4, IPv6, IPv4 … 方式交错,避免一次只尝试同族地址导致长阻塞。
③ 渐进并发拨号 对首地址立即 connect();等待 250 ms(RFC 8305 推荐) 后对下一地址发起第二条连接;依次递进,直至某条连接 ready
④ 选定胜者 第一条进入 ESTABLISHED(或 TLS 完成)的连接即被采用;其它尝试全部 cancel()
⑤ 记忆优化(可选) 统计哪种族群成功率更高,下次先排优胜族群,减少不必要并发。

RFC 8305 还扩展到:
• 自带多 DNS 服务器场景的查询竞争
• 多端口 / 多协议(TCP, UDP, QUIC)并发
• 对移动网络切换的地址集动态更新


3 为什么叫 “Happy Eyeballs”?

IETF 文档把终端用户比喻成 eyeballs。算法通过让浏览器 / App “看见” 页面更快,使用户(眼球)“happy”。(en.wikipedia.org)


4 苹果生态里的实现现状
API / 组件 是否自带 Happy Eyeballs 备注
CFNetwork / NSURLSession ✔︎ iOS 5 起引入 v1,iOS 11 + 升级为 v2
Network.framework (NWConnection) ✔︎ 底层已实现;App 无需额外处理
getaddrinfo + 自行 connect 需要自己在应用层复刻算法

因此:**你只要用系统连接栈,早已自动享受 Happy Eyeballs;**只有在手写拨号器(自建 QUIC / Raw TCP)或为了测 IP 而逐一测试时,才需自己实现竞速逻辑。


5 自定义实现简易伪代码(Swift Concurrency)
func happyConnect(ips: [String], port: UInt16) async throws -> Socket {
            
    let delay: UInt64 = 250_000_000  // 250 ms
    var tasks: [Task<Socket, Error>] = []
    for (index, ip) in ips.enumerated() {
            
        let task = Task.detached {
             try Socket.connect(ip, port: port) }
        tasks.append(task)

        if index == 0 {
             continue }
        try await Task.sleep(nanoseconds: delay)
    }

    do {
            
        let winner = try await withTaskGroup(of: Socket.self) {
             group -> Socket in
            for t in tasks {
             group.addTask {
             try await t.value } }
            let socket = try await group.next()!
            group.cancelAll()     // 其他并行尝试全部放弃
            return socket
        }
        return winner
    } catch {
            
        throw error      // 全部失败再抛
    }
}

关键点: 交错地址顺序 + 渐进并发 + 及时取消


6 调优与陷阱
问题 建议
并发过多占端口 / 电量 最多同时保持 2 ~ 3 条尝试足矣(RFC 8305 §4.2)
移动网络切换 监听 NWPathMonitor,切网后重新解析 & 重新跑竞速
TLS 0-RTT / QUIC 竞速结束后再发应用层首包,避免对失效流量浪费数据
日志指标 埋点记录 “赢得竞速的地址族 + RTT” 评估网络质量

7 结语

Happy Eyeballs 通过 “IPv6 优先但绝不错过 IPv4” 的并发竞速,让终端在复杂双栈网络里依旧秒开连接,是现代浏览器和 iOS 网络栈的标配。

如果你只是用 NSURLSession,无需操心;如果你要自己做智能选路 / 测速,请把 RFC 8305 的节奏照搬,别让用户的眼球再“哭泣”。


参考

RFC 8305 — Happy Eyeballs v2 (datatracker.ietf.org)
RFC 6555 — Happy Eyeballs: Success with Dual-Stack Hosts (datatracker.ietf.org)
Wikipedia:Happy Eyeballs 概述与历史 (en.wikipedia.org)

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容