最近在折腾 Home Assistant (HASS) 的公网穿透。我使用了 Cloudflare Tunnel,但发现了一个诡异的现象:本地访问秒开,CF 访问却转圈。在排查过程中,我从应用层的 HTTP 一路杀到了传输层的 TCP,甚至重新认识了 MySQL 这种老牌协议。
一、 协议的门派:为什么 MySQL 必须走 TCP?
在穿透 HASS(HTTP)时,我们觉得理所当然,但当我尝试穿透 MySQL 时,碰了壁。
1. 应用层代理(L7)的局限 HTTP 代理像是一个懂多种语言的翻译官,它能听懂网页请求。但 MySQL 是私有二进制协议,它有一套专属的“暗号”。
- HTTP 代理: 听不懂 MySQL 在说什么,直接拒收。
- TCP 转发(L4): 像一个搬砖工,不拆包,只管把数据流从 A 端搬到 B 端。
结论: 凡是像 MySQL、SSH 这样不走 HTTP 路线的私有协议,在穿透时必须回归到传输层代理(TCP Forwarding)。
二、 核心博弈:有状态 (Stateful) vs 无状态 (Stateless)
理解了 TCP 转发后,我发现了一个更有趣的概念:状态。
- HTTP 的“渣男”属性(无状态): 请求完就忘。为了记住你是谁,我们得给它挂载 Cookie 和 Session。
- TCP 的“执着”属性(有状态): 每一个连接都有一个“五元组”身份标识。它必须维持一个长期的会话。
MySQL 为什么不选 HTTP? 因为它需要极其严苛的“有状态”环境。事务(Transaction)处理需要服务器死死记住你刚才锁了哪行数据。这种深度的上下文绑定,是无状态的 HTTP 无法低成本提供的。
三、 实战避坑:Cloudflare Tunnel 的调优
在折腾 cloudflared 的过程中,我通过日志发现了两个让穿透变慢的“元凶”:
1. UDP 被运营商 QoS
默认的隧道协议可能会被运营商限速。
- 手术操作: 强制开启
--protocol http2。 - 效果: 将不稳定的 UDP 握手转为成熟的 TCP 连接,体感延迟瞬间下降。
2. 地理位置的“随机抽奖”
我在日志里看到了 hkg(香港)和 sjc(圣何塞)。
- 扎心真相: 如果流量被随机分配到了美国圣何塞,数据包就要跨越太平洋。
- 对策: 通过设置代理环境变量(如
http_proxy),配合 V2Ray 等工具,强行将隧道连接锁定在亚洲节点。
四、 进阶:当网络切换时,我们在经历什么?
最后,我研究了从 Wi-Fi 切换到 5G 时的断连问题。
由于 TCP 是有状态的,IP 一变,连接即毁。这也是为什么 HASS 页面会卡死。
- 未来的解药: HTTP/3 (QUIC)。它不再基于 IP,而是基于 Connection ID。
- 感悟: 技术的发展,本质上就是在不断修补底层协议“太有状态”或“太没状态”所带来的副作用。
五、 WebSocket:披着 HTTP 皮的 TCP 灵魂
在排查 HASS 穿透时,你可能会问:既然 HTTP 是无状态的,为什么我点击开关,手机能实时收到状态反馈?
这就是 WebSocket 的功劳。它是我们这篇探讨中“状态”的集大成者。
- 借壳上市(握手阶段):
WebSocket 起初是一个普通的 HTTP 请求(无状态)。它通过在 Header 里写上
Upgrade: websocket,像是在问服务器:“咱们能不能换个持久的方式聊天?” - 定情终身(通信阶段):
一旦握手成功,它就彻底撕下了 HTTP 的伪装,回归了 TCP 的本质。它变成了一个双向、实时的管道。
- 为什么快? 它省去了 HTTP 频繁的 Header 传输。
- 为什么怕断? 因为它是有状态的。它死死绑定在底层的那条 TCP 连接上。
六、 总结:网络世界的“状态”真相
通过这次对 Cloudflare Tunnel 的深度调优,我们可以把学到的知识串成一条线:
- 传输层 (TCP): 它是地基。它是有状态的,负责保证包裹不丢(MySQL、SSH、WebSocket 都离不开它)。
- 应用层 (HTTP): 它是快餐。它是无状态的,负责简单快速地分发内容(网页、图片、API)。
- 特殊的应用层 (MySQL): 它是老古董。它直接在 TCP 上跑二进制,因为 HTTP 的无状态和文本格式对它来说太沉重了。
- 现代的应用层 (WebSocket): 它是混合体。它用 HTTP 开头,用 TCP 的方式持久运行,专为实时监控(HASS)而生。
七、 最终实战心得:如何让穿透稳如泰山?
如果你也像我一样,通过 Cloudflare Tunnel 访问家里的服务,请记住这三条黄金法则:
- 强制协议转换: 将
cloudflared的协议改为--protocol http2。这能让你的隧道在复杂的公网环境中,从脆弱的 UDP 转向成熟稳定的 TCP,从而保护上层的 WebSocket 不轻易断连。 - 路由分流: 利用 V2Ray 或代理,把隧道终点锁在亚洲(如香港)。物理距离(RTT)是实时交互最大的敌人,再好的协议也跑不过光速。
- HASS 安全策略: 务必在 HASS 的
trusted_proxies中添加隧道 IP。如果握手因为安全校验慢了 1 秒,上层的 WebSocket 体验就会大打折扣。
结语
技术圈常说“大道至简”,但真实的互联网底层却充满了各种为了平衡“性能”与“扩展性”而做的妥协。
从 MySQL 的二进制执着,到 HTTP 的断舍离,再到 WebSocket 的重修旧好,以及 Cloudflare Tunnel 在中间的巧妙撮合。理解了这些,你就理解了为什么在网络切换的一瞬间,你的智能家居会短暂“失灵”。
底层决定上层,状态决定实时。
Leave a comment