1. TLS握手基础:回顾TLS 1.2/1.3握手流程,明确超时发生的阶段

做网络排障这些年,我见过太多人一遇到TLS超时就直接甩锅给网络。其实很多时候,问题出在握手阶段本身。你想想看,TLS握手就像两个人初次见面——先打招呼、交换身份、商量用什么暗号、最后确认彼此都准备好了。任何一个环节卡住,连接就超时了。

这一章,咱们先把TLS 1.2和1.3的握手流程过一遍。不是为了背协议,而是为了搞清楚:超时到底可能发生在哪个阶段?

1.1 TLS 1.2 握手:四次握手,步步惊心

TLS 1.2的握手,说白了就是客户端和服务端来回折腾4次消息交换。我刚开始做HTTPS优化时,总觉得这流程太啰嗦,后来在项目里踩过坑才明白——每一步都有它的道理。

核心要点:TLS 1.2握手至少需要2个RTT(网络往返时间)。如果网络延迟是100ms,光握手就要200ms,还没算上证书验证和密钥计算的时间。

完整的TLS 1.2握手流程如下:

  1. ClientHello — 客户端发起握手,告诉服务端它支持的TLS版本、密码套件列表、以及一个随机数。
  2. ServerHello + 证书 + ServerHelloDone — 服务端选好密码套件,发回自己的证书,并说“我说完了”。
  3. ClientKeyExchange + ChangeCipherSpec + Finished — 客户端验证证书,生成预主密钥,用服务端公钥加密后发过去,然后通知“我要开始加密通信了”。
  4. ChangeCipherSpec + Finished — 服务端解密预主密钥,生成会话密钥,也通知“我也准备好了”。

我个人的经验:在实际排障中,TLS 1.2握手超时最常见的位置是第2步和第3步之间。为什么?因为证书链太长或者OCSP(在线证书状态协议)响应慢,都会卡在这里。我曾经遇到过一个客户,证书链里嵌了5层中间证书,每次握手都要多花800ms去下载和验证——这能不超时吗?

1.2 TLS 1.3 握手:一回合搞定

TLS 1.3最大的改进,就是把握手从2个RTT压缩到了1个RTT。怎么做到的?说白了就是“预判”。客户端在第一个消息里就把密钥协商的材料准备好了,服务端收到后直接算出密钥,一次往返就能开始加密通信。

流程简化成这样:

  1. ClientHello — 除了版本和密码套件,还带上了密钥共享参数(比如ECDHE的临时公钥)。
  2. ServerHello + 证书 + Finished — 服务端选好参数,发回自己的密钥共享和证书,然后直接说“我准备好了”。
  3. 客户端Finished — 客户端验证完证书,也确认“我准备好了”。

你看,少了整整一次往返。而且TLS 1.3砍掉了很多不安全的密码套件,只保留了前向安全的几个选项。嗯,这里要注意——虽然握手快了,但超时问题并没有消失,只是转移了位置。

对比项 TLS 1.2 TLS 1.3
最少RTT 2 RTT 1 RTT(0-RTT可选)
握手消息数 4次(不含ACK) 3次(不含ACK)
密码套件协商 客户端列出全部,服务端选择 客户端只列出支持的密钥交换和签名算法
证书发送时机 ServerHello之后立即发送 ServerHello之后立即发送
常见超时点 证书验证、密钥交换 证书验证、0-RTT数据重放

1.3 超时到底发生在哪个阶段?

搞清楚了流程,咱们来划重点。TLS握手超时,本质上就是某个消息没能在预期时间内到达或处理完成。我把它分成三个阶段:

  • 阶段一:连接建立前(TCP三次握手阶段) — 这其实不是TLS的问题,但很多人混淆了。TCP没连上,TLS根本启动不了。
  • 阶段二:握手消息交换阶段 — 这是TLS超时的重灾区。包括ClientHello丢失、ServerHello延迟、证书传输卡顿、Finished消息没收到。
  • 阶段三:握手后验证阶段 — 比如服务端等待客户端发送应用数据,但客户端还在验证证书,导致服务端认为握手超时。

避坑指南:我曾经在排查一个线上问题时,发现客户端日志显示“TLS握手超时”,但抓包一看,TCP三次握手都没完成。所以记住:先确认TCP连接是否建立,再谈TLS握手超时。别被应用层的错误日志带偏了。

1.4 一张图看懂TLS握手与超时风险点

下面这张SVG图,把TLS 1.2和1.3的握手流程画在一起,并标出了最容易超时的位置。我建议你保存下来,以后排障时直接对照。

TLS 1.2 vs 1.3 握手流程与超时风险点 客户端 服务端 TLS 1.2(左侧) vs TLS 1.3(右侧) ① ClientHello ② ServerHello + 证书 ⚠ 证书传输慢 ③ ClientKeyExchange ④ Finished ⚠ 密钥计算慢 ① ClientHello + 密钥共享 ② ServerHello + 证书 + Finished ⚠ 0-RTT重放 TLS 1.2 客户端消息 TLS 1.2 服务端消息 TLS 1.3 客户端消息 TLS 1.3 服务端消息 超时风险点

1.5 超时时间到底设多少?

这个问题没有标准答案,但我可以给你一个参考范围。不同的操作系统和库,默认超时时间差别很大:

组件 默认超时 说明
Linux TCP连接超时 20-127秒(取决于重传) 由tcp_syn_retries控制
OpenSSL握手超时 无默认值,由应用层设置 通常建议30秒
Nginx proxy_connect_timeout 60秒 与后端建立连接的超时
Java SSLSocket超时 0(无限等待) 必须显式设置setSoTimeout
浏览器(Chrome/Firefox) 30-60秒 不同版本略有差异

我个人的建议:生产环境中,TLS握手超时不要设得太短,也别太长。10秒太激进,遇到证书链稍长一点就误报;120秒又太拖沓,用户体验极差。我一般推荐30秒作为起点,然后根据实际网络延迟和证书链长度微调。如果大部分请求在200ms内完成,30秒的超时已经足够宽容了。

1.6 小结:超时排查的起点

这一章咱们把TLS握手的基础打牢了。记住几个关键点:

  • TLS 1.2需要2个RTT,TLS 1.3只需要1个RTT——但超时风险依然存在。
  • 超时最常发生在证书传输和密钥交换这两个环节。
  • 排查超时前,先确认TCP连接是否已经建立——别被误导。
  • 超时时间设置要结合实际场景,30秒是个不错的起点。