在 HTTP 通信中,Keep-Alive(持久连接)是一个至关重要的性能优化机制。它解决了“每发一个请求都要重新握手”带来的巨大开销。
1. Keep-Alive 的作用
在早期的 HTTP/1.0 中,每个请求/响应周期都会经历:建立 TCP 连接 -> 发送请求 -> 接收响应 -> 断开连接。
如果一个页面有 50 张图片,手机就要进行 50 次三次握手和 50 次四次挥手。Keep-Alive 的作用就是允许在同一个 TCP 连接上发送多个 HTTP 请求/响应。
- 减少延迟:省去了重复建立 TCP 连接的时间(特别是 HTTPS 的 TLS 握手开销)。
- 降低 CPU/内存消耗:服务器和客户端不需要频繁创建和销毁 Socket。
- 缓解拥塞:减少了网络中的控制包数量。
2. iOS 中的实现方式
在 iOS 中,你不需要手动去维护 TCP 连接的开启与关闭,URLSession 已经高度自动化地处理了 Keep-Alive。
A. 默认行为 (URLSession)
URLSession 默认是开启 Keep-Alive 的。它维持着一个连接池(Connection Pool),会自动复用那些尚未空闲超时的连接。
- HTTP/1.1:默认开启。通过
Connection: keep-alive请求头实现。 - HTTP/2 & HTTP/3:天然支持多路复用,不需要显式的 Keep-Alive 头,但底层的连接持久化逻辑是一致的。
B. 如何配置持久连接参数
你可以通过 URLSessionConfiguration 来微调连接的行为:
Swift
let config = URLSessionConfiguration.default
// 1. 设置最大并发连接数(针对每个 Host)
// HTTP/1.1 默认为 6。如果设为 1,则所有请求会排队复用同一个连接。
config.httpMaximumConnectionsPerHost = 6
// 2. 设置连接超时(建立连接的等待时间)
config.timeoutIntervalForConnect = 30
// 3. 设置请求超时(数据传输的等待时间)
config.timeoutIntervalForRequest = 60
C. 如何手动关闭 Keep-Alive
虽然极少需要这样做,但如果你想强制服务器在响应后关闭连接,可以手动设置 Header:
Swift
var request = URLRequest(url: url)
request.setValue("close", forHTTPHeaderField: "Connection")
3. 注意事项与“坑”
- 服务器端的超时:即便 iOS 端想保持连接,如果服务器设置了
Keep-Alive: timeout=5(5秒后自动断开),那么 5 秒后的下一个请求依然需要重新握手。 - 后台挂起:当 iOS App 进入后台时,系统为了省电会断开所有的 TCP 连接。因此,App 回到前台后的第一个请求通常会比较慢,因为需要重新握手。
- 连接泄露:如果你在代码中不断创建新的
URLSession(而不是复用同一个单例 session),那么每个 session 都会维护自己的连接池,导致 Keep-Alive 失去意义,甚至耗尽系统资源。
总结
- Keep-Alive 是为了“一次握手,多次使用”。
- 在 iOS 中,请务必复用
URLSession对象(通常建议单例化),以充分发挥连接复用的优势。