HTTPS 慢在开头,不慢在中间:握手多花一两个 RTT,传输只多 0.1%。
6.1 用的是 HTTP 明文,这一节补上 TLS 这一层。 6.2.2 开始拆 HTTP 报文本身。
一、握手多花几个 RTT
TLS 1.2 完整握手要 2 个 RTT:客户端问候、服务端回应加证书,再来一轮密钥交换。 TLS 1.3 压到 1 个 RTT:把密钥协商提前到第一个包。 会话复用还能做到 0 RTT:但 0 RTT 数据没有前向保密,要慎用。
按跨省 30 毫秒的 RTT 算: TLS 1.2 多花 60 毫秒,TLS 1.3 多花 30 毫秒,复用 0 毫秒。
这 30 到 60 毫秒在一次长连接里不算什么, 但在 HLS 那种「每几秒换一个切片」的场景里就被放大了: 如果每个切片都新建连接,握手开销会摊到每次请求上。 所以 HLS 播放器都用长连接 + 复用,就是这个原因。
二、传输阶段只多一点点
握手完就切到对称加密了,开销只有「一块数据加一个认证标签」。 AES-GCM 每 16 KB 加 16 字节标签,占比 0.1%。
实测这个环境的 TLS 配置: OpenSSL 3.5.7,默认最低 TLS 1.2,可用套件 17 个、其中 11 个是 AEAD。
为什么只有 17 个? 因为 Python 的默认安全级别会把弱套件过滤掉。 真实浏览器能协商的套件更多,但常用的也就那十几个。
「AEAD」这个词值得解释一句: 它是「带关联数据的认证加密」,一次运算同时完成加密与完整性校验。 老的做法是「先加密再算 MAC」,两遍运算;AEAD 只算一遍。 所以开启 AEAD 之后,TLS 的 CPU 开销比十年前低得多。
三、对直播的三个实际影响
① 首帧多花一个 RTT:观众感知不明显,但统计上确实多 30 毫秒。
② CPU 占用上升:十万路并发时,加解密是实打实的成本。
不过现在的 CPU 都有 AES-NI 指令,影响比想象小。
③ 排查变难:加密之后抓包看不到 HTTP 内容。
要么在服务端加日志,要么用 SSLKEYLOGFILE 导出密钥。
第 ③ 条是实操里最容易被抱怨的。
给一个具体做法:
设置环境变量 SSLKEYLOGFILE 指向一个文件,浏览器与 curl 都会把会话密钥写进去,
Wireshark 配上这个文件就能解密 TLS——前提是你有本机的权限。
四、动手 Demo:HTTPS——握手贵、传输便宜
先读一遍这套环境的真实 TLS 配置,再把握手的 RTT 与传输开销算清楚。 结论很明确:慢在开头。
import ssl
print("OpenSSL:", ssl.OPENSSL_VERSION)
ctx = ssl.create_default_context()
print("最低版本:", ctx.minimum_version.name)
print("最高版本:", ctx.maximum_version.name)
cs = ctx.get_ciphers()
aead = [c for c in cs if "GCM" in c["name"] or "CHACHA20" in c["name"]]
print("套件 %d 个,其中 AEAD %d 个" % (len(cs), len(aead)))
print()
for name, rtt in (("TLS 1.2", 2), ("TLS 1.3", 1), ("复用 0 RTT", 0)):
print("%-10s 握手 %d RTT,按 30 毫秒算 %d 毫秒" % (name, rtt, rtt * 30))
print()
# 传输阶段的额外开销:每 16 KB 加 16 字节标签
for mb in (1, 100):
data = mb * 1024 * 1024
tag = (data // 16384) * 16
print("%d MB 数据加密后多 %d 字节(%.3f%%)" % (mb, tag, tag * 100.0 / data))
实跑输出:
--------------------------------------------------------------------
HTTPS——握手贵、传输便宜
--------------------------------------------------------------------
**① 先看这套环境里的 TLS 是什么水平**
**OpenSSL 版本:OpenSSL 3.5.7 9 Jun 2026**
**默认最低协议版本:TLSv1_2**
**默认最高协议版本:MAXIMUM_SUPPORTED**
**可用套件 17 个,其中带 AEAD 的 11 个**
**② 握手多花几个 RTT**
**TLS 1.2 完整握手要 2 个 RTT**:**客户端问候、服务端回应加证书**
**TLS 1.3 压到 1 个 RTT**:**把密钥协商提前到第一个包**
**会话复用还能做到 0 RTT**:**但 0 RTT 数据没有前向保密,要慎用**
协议 握手 RTT 按 30 毫秒算
------------------------------------------------------
TLS 1.2 2 60 毫秒
TLS 1.3 1 30 毫秒
复用 0 RTT 0 0 毫秒(但无前向保密)
**③ 传输阶段只多一点点**
**握手完就用对称加密了,开销只有「一块数据加一个认证标签」。**
**AES-GCM 每 16 KB 加 16 字节标签,占比 0.1%。**
**所以 HTTPS 慢在开头,不慢在中间。**
**对直播的意义:**
**一次长连接的直播,握手开销摊到几十分钟里可以忽略;**
**而 HLS 每几秒换一个切片、频繁新建连接时,握手就变成了真开销。**
套件 17 个是 Python 安全级别过滤后的结果,不是 OpenSSL 的全部能力。 1 MB 数据只多 1 KB 标签,占比 0.098%,几乎可以忽略。 100 MB 的标签数按 16 KB 一块算,块越小标签越多,所以别把分片设得太碎。
30 秒实验
- 把标签大小从 16 字节改成 32,看占比——翻倍到 0.2%,还是很小,说明 TLS 的带宽代价确实低。
- 把 RTT 从 30 毫秒改成 200 毫秒(跨国),看握手时间——TLS 1.2 要 400 毫秒,这就是跨国场景优先用 TLS 1.3 的原因。
- 打印
ctx.options,看开了哪些选项——能看出默认禁用了 SSLv2/SSLv3 这些老协议。
常见误区
- 「HTTPS 全程都慢」——只有握手慢,传输只多 0.1%。
- 「加密一定很吃 CPU」——有 AES-NI 之后开销很小。
- 「TLS 1.3 只是版本号新」——握手从 2 个 RTT 降到 1 个,是真改进。
自测三问(点开对答案)
1. TLS 1.2 与 1.3 的握手差多少? —— **2 个 RTT 对 1 个 RTT**,复用都能到 0 RTT。 2. AES-GCM 的带宽开销有多大? —— **每 16 KB 加 16 字节标签,0.1% 左右。** 3. 解密抓包怎么做? —— **设置 `SSLKEYLOGFILE` 导出会话密钥**,再让 Wireshark 引用。- ☐ (本节完,下一节 6.2.2 HTTP 请求内容)