搜索 K
Appearance
关于「Mac 合盖再打开,代理就废了」这件事,社区里流传的解释大多跑偏。基于我们过去两年在几十台不同机型的复现记录,有三条结论可以直接拿走:
第一,这几乎从来不是节点的问题。 你换十个机场,症状会一模一样——Safari 转圈、curl 卡在 TLS 握手、Telegram 显示"Connecting…"。因为根因在本地:代理进程持有的 socket 句柄、TUN 接口的路由表项、系统 DNS 的 resolver 状态,这三者在睡眠期间被打散了,唤醒后没有原子性地重建。
第二,最高效的修复动作是「重启代理进程」,而不是「重连 Wi-Fi」或「刷 DNS」。 重连 Wi-Fi 只会重建物理层,代理进程里的死连接依旧挂在进程内存里;dscacheutil -flushcache 只解决 DNS 层,对 TCP 黑洞毫无作用。真正能一次性清干净所有状态的,只有让进程重新走一遍启动流程。
第三,要根治,需要三件套: pmset 电源策略调优(减少 DarkWake 与深层待机)+ 一个幂等的守护脚本(不依赖唤醒事件,靠健康探测驱动)+ 一个服务端 NAT 表项足够长、路径足够稳的机场。前两者是自己能控制的,第三者取决于你选谁。
下文会按「物理机理 → 量化矩阵 → 客户端实操 → 自动化脚本 → 抓包排障 → 避坑 → FAQ」的顺序展开。只想抄脚本的,可以直接跳到第六章;想搞明白为什么的,从第二章开始。
很多人把「睡眠」当成一个开关,其实 Darwin 的电源管理是一套分层状态机,用 pmset -g 可以直接看到当前策略:
hibernatemode 3(笔记本)会把内存镜像写一份到磁盘作为保险。关键点在于:DarkWake 期间系统不会通知用户态应用做网络重连。你的代理进程可能被 SIGSTOP 挂起,网卡被关掉,几秒后网卡回来了,但进程里那批 TCP 连接还"以为"自己活着。
按时间顺序:
0.0.0.0/1 和 128.0.0.0/1 这两条经典的 TUN 劫持路由。scutil --dns 会看到 resolver 列表按接口优先级重排,DHCP 下发的 DNS 可能重新排到最前。做完这一套,快则 300ms,慢则 2s。而代理进程的 utun 接口,如果是直接操作的(比如 Clash 系用 tun + 系统路由注入),这六步里没有任何一步会通知它。
把复现记录归类,卡死基本逃不出这三种:
A 类:socket 僵死(最常见,约 60%) 进程活着、端口在听、utun 在跑,但所有 TCP 连接处于"已建连但黑洞"状态。表现是 curl 一直等到超时才失败,不报 Connection Refused。根因是睡眠期间对端或中间设备(NAT、防火墙)已经回收了会话,而本机没收到 RST/FIN,socket 仍处于 ESTABLISHED。
B 类:路由丢失(约 25%) utun 接口还在 ifconfig 里,但 netstat -rn 里指向它的默认路由没了。表现是 ping 公网 IP 通、ping 域名失败,或者干脆全不通。根因是内核重建路由表时没把 TUN 的路由恢复回来。
C 类:DNS 回落(约 15%) TCP 层是通的,但域名解析走了 ISP DNS,导致被污染或解析到错误 IP。表现是"国内站飞快、国外站超时",或者 fake-ip 段(198.18.0.0/16)失效后返回真实 IP 被墙。
分辨这三类,靠的是第七章那张判定表,不要凭感觉猜。
本地修完了,还有一半变量在服务端。
NAT 表项老化时间是硬伤。一台中转机上的 conntrack 表,UDP 默认 30 秒、TCP ESTABLISHED 默认 86400 秒(5 天)在 Linux 上是常见配置,但云厂商的安全组、SLB、iptables 往往把 TCP 空闲超时压到 300~900 秒。你睡 8 小时,醒来时服务端早把你的会话清了。
拥塞控制算法在这里起作用。CUBIC 对"连接已死但无 RST"的判定依赖 RTO 退避,可能要重传两三轮(几百毫秒到数秒)才放弃;BBRv3 的探测机制更快收敛,且在高丢包路径上能保住带宽利用率。但注意——BBR 只能加速"识别失败",不能"复活"一个已经被中间设备回收的五元组。
IEPL / IPLC 专线的价值在这里体现得最明显:二层/三层专线路径上没有第三方 NAT 设备,不存在中途被静默回收会话的情况,重连时也不需要重新穿越公网 BGP 收敛。这就是为什么企业级专线用户很少抱怨"唤醒卡死"——他们的路径本身就是干净的。
双 ISP / 多线 BGP 入口解决的是另一个问题:唤醒后你的公网 IP 变了(Wi-Fi 漫游、运营商 CGNAT 重分配),如果服务端只有单一入口,某些运营商的回程路由可能变差。多线入口能让客户端自动匹配到延迟更低的路径。
| 判定维度 | A 类 socket 僵死 | B 类 路由丢失 | C 类 DNS 回落 |
|---|---|---|---|
| 占复现样本比例 | 约 60% | 约 25% | 约 15% |
curl 直连超时阈值 | 10~30 s 才失败 | 立即失败或直接不通 | 3~8 s 解析失败 |
netstat -rn 默认路由 | 正常指向 utun | 丢失或指向 en0 | 正常 |
| `scutil --d |