网络协议
网络是中级面试必考硬通货. 如果你有风控, 抓包对抗或 TLS 相关的可核验经历, 可用 HTTPS, 证书, TLS 以及中间人防护, 证书校验, 双向认证等内容说明经验边界; 否则按本篇知识点和练习证据准备.
本篇含知识点讲解 + 高频面试题.
一, HTTP 基础
- 无状态: 每次请求独立, 靠 Cookie/Session/Token 维持状态.
- 请求结构: 请求行 (方法 + URL + 版本)+ 请求头 + 空行 + 请求体.
- 常用方法: GET (查, 幂等), POST (增, 非幂等), PUT (全量改, 幂等), DELETE, PATCH (部分改), HEAD, OPTIONS (预检).
- GET vs POST: 两者首先由方法语义区分, 不是 “参数放哪” 的固定规则. GET 通常用于安全, 幂等的资源读取, 请求内容常编码在 URI / 查询参数中; 实际 URI 长度受客户端, 代理和服务端实现限制, HTTP 规范没有一个通用固定上限. POST 用于让目标资源按请求内容执行处理, 默认不幂等, 但可以通过明确的新鲜度信息和缓存键被缓存; 请求体也受服务器, 网关, 客户端和业务配置限制, 不能说 “无长度限制”.
状态码
- 1xx 信息;2xx 成功 (200, 201, 204).
- 3xx 重定向: 301 (永久), 302 (临时), 304 (协商缓存命中).
- 4xx 客户端错: 400, 401 (未认证), 403 (无权限), 404, 405, 429 (限流).
- 5xx 服务端错: 500, 502 (网关错误), 503 (不可用), 504 (网关超时).
逐码的排障定位, 重试与退避策略见 见 30.
缓存
- 强缓存:
Cache-Control(max-age),Expires, 不发请求直接用本地. - 协商缓存:
ETag/If-None-Match,Last-Modified/If-Modified-Since, 发请求由服务端判断, 命中返 304.
报文逐行解读与登录态边界
下面是数字示例, 用于解释一对 HTTP/1.1 POST/200 报文, 非可直接执行代码.
POST /v1/sessions HTTP/1.1
Host: api.example.test
Accept: application/json
Content-Type: application/json
Content-Length: 15
{"email":"a@b"}
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 11
Cache-Control: no-store
{"ok":true}
请求行给出方法/目标/版本; Host 路由虚拟主机; Content-Type 说明 body 表示; 请求 JSON 的 UTF-8 字节数为 15, 因此 Content-Length: 15. 响应 body {"ok":true} 是 11 字节, 故为 Content-Length: 11. no-store 禁止保存敏感响应; no-cache 是复用前重验证. max-age 到期后可携带 ETag 的 If-None-Match, 匹配时 304 且复用旧 body.
HTTP/1.1 也可用 Transfer-Encoding: chunked: 每块以十六进制长度, CRLF, 数据, CRLF 传输, 最后以 0 块结束. 接收端不得同时依赖 Content-Length 和 Transfer-Encoding 定界: CL/TE 冲突可能导致 request smuggling, 应按 RFC/网关策略拒绝或规范化. 没有 CL/TE 时响应可由连接关闭定界, 但不适用于可复用连接. HTTP/2/3 使用各自帧层, 不使用 HTTP/1.1 chunk framing; 204, 304 和对 HEAD 的响应不得带 message body.
Cookie 是客户端随匹配域 / 路径规则自动附带的状态载体; Session 是服务端用 cookie 值关联的状态; Token 是客户端显式放入如 Authorization: Bearer <ACCESS_TOKEN> 的凭据格式, 未必天然无状态. 移动端应使用 HTTPS, 限制 token 生命周期和刷新流程, 日志只保留是否存在 / 哈希或长度等诊断信息, 不能输出原文.
Cookie 的 Domain 决定可发送的主机范围, 省略时为 host-only; Path 限制路径但不是访问控制; Secure 仅经 HTTPS 发送; HttpOnly 阻止脚本读取; SameSite 限制跨站自动携带; Expires/Max-Age 决定持久期. 登录成功, 提权和密码变更后轮换 session ID, 防 session fixation.access token 应短期, 仅通过 TLS 发送; refresh token 仅在受控刷新端点使用, 可轮换/撤销, 移动端放在按威胁模型选择的受保护存储而不是日志/URL. 登录, 刷新, 注销以关联 trace 记录事件结果和 token 版本: 登录签发 access/refresh, access 过期仅走单一刷新流程, 刷新失败或注销即撤销 refresh 并清除本地凭据; 不能记录 token 原文. 撤销 refresh 和删除本地凭据不会使已经签发的自包含 access token 立即无效, 默认只能等它到期; 高风险系统应使用更短 TTL, 并按风险采用 denylist, token version 校验或可撤销的引用 token. Cookie 自动附带时需防 CSRF; 将 token 置入可被脚本读取的位置会扩大 XSS 泄漏风险; 二者都不能被 “使用 HTTPS” 替代.
自测: (1) 用 curl --raw 向测试服务发送 chunked body, 预期服务按块重组; (2) 发送 CL/TE 冲突报文, 预期网关拒绝而非转发歧义请求; (3) 登录后检查 session 轮换, 注销后 refresh token 被拒绝, 日志只含 trace 和结果.
二, TCP/IP 分层与数据封装
TCP/IP 是面向实际网络通信的四层抽象, 与 OSI 七层模型不是逐层一一对应的关系. 例如 OSI 的会话层和表示层常由 TCP/IP 应用层协议或应用程序承担, 排障时应先说明所用模型, 不要把两者硬性等同.
| TCP/IP 层 | 主要职责 | 常见协议或机制 | 数据单元 |
|---|---|---|---|
| 应用层 | 定义业务语义, 消息格式和应用认证 | HTTP, HTTPS, DNS, WebSocket | 数据 / 报文 |
| 传输层 | 端到端进程通信, 端口复用与传输语义 | TCP, UDP, QUIC | TCP 段 / UDP 数据报 / QUIC 包 |
| 网际层 | 跨网络寻址与路由转发 | IP, ICMP | IP 数据报 |
| 网络接口层 | 在本地链路上传送帧 | Ethernet, Wi-Fi, ARP | 帧 |
一次 HTTPS 请求的封装可按下列路径理解: 应用数据先形成 HTTP 报文, 通常交给 TLS 记录层保护, 再由 TCP 分段, 封装进 IP 数据报和链路帧. 接收端按相反方向解封装. HTTP/3 的路径不同: HTTP/3 报文由 QUIC 承载, QUIC 包再进入 UDP 数据报; QUIC 在用户态实现连接, 可靠传输, 流控制和流的有序交付等能力, UDP 只提供无连接的数据报承载.
HTTP/1.1 或 HTTP/2 over HTTPS:
HTTP data -> TLS records -> TCP segments -> IP packets -> link frames
HTTP/3:
HTTP/3 data -> QUIC packets -> UDP datagrams -> IP packets -> link frames
HTTPS, TCP, UDP 与 Socket 的边界
HTTPS 是 HTTP 叠加 TLS 的应用层协议. TCP 和 UDP 是传输层协议. Socket 不是网络协议, 而是操作系统提供的编程接口抽象: 应用通过 socket 选择地址族和传输类型, 再读写字节流或数据报. 一个常见的 HTTPS 客户端通常使用 TCP socket; 支持 HTTP/3 的客户端则使用 UDP socket 与 QUIC 栈通信.
| 对象 | 所在抽象层 | 连接与交付语义 | 加密 | 常见适用场景 |
|---|---|---|---|---|
| HTTPS | 应用层 | HTTP 语义; 通常经 TLS over TCP 可靠有序传输, HTTP/3 时经 QUIC | TLS 提供 | 登录, 支付, API 和网页 |
| TCP | 传输层 | 面向连接, 可靠, 有序字节流 | 不自带 | 文件传输, 传统 HTTP/HTTPS |
| UDP | 传输层 | 无连接数据报, 不保证到达, 顺序或去重 | 不自带 | DNS, 实时音视频的媒体数据, QUIC 承载 |
| Socket | OS 编程接口 | 取决于选择 TCP, UDP 或其他协议族 | 取决于上层协议 | 客户端, 服务端和本地进程间通信 |
不能因为 UDP 不提供 TCP 的可靠性保证就称 UDP “不安全”: 机密性和身份认证由 TLS 等安全协议决定, 可靠性, 顺序和拥塞控制也可以由 QUIC 或应用协议按业务需求实现. 反过来, TCP 可靠有序也不等于加密; 未叠加 TLS 的 TCP 流量仍可能被窃听或篡改.
三, HTTP 版本演进
| 版本 | 关键特性 | 解决的问题 |
|---|---|---|
| HTTP/1.0 | 短连接, 每次请求新建 TCP | — |
| HTTP/1.1 | 长连接 (keep-alive), 管线化, Host 头, 分块传输 | 复用连接 |
| HTTP/2 | 二进制分帧, 多路复用 (一个连接并发多请求), 头部压缩 (HPACK) | 队头阻塞 (应用层) |
| HTTP/3 | 基于 QUIC over UDP, 0-RTT, 连接迁移 | TCP 队头阻塞, 握手延迟 |
- HTTP/1.1 队头阻塞: 同一连接请求需按序响应, 前一个慢会卡后面. HTTP/2 多路复用在应用层缓解, 但 TCP 层仍有队头阻塞; HTTP/3 基于 QUIC/UDP, 通过独立 stream 降低 TCP 层队头阻塞影响.
- WebSocket: 提供双向消息通道. HTTP/1.1 常通过 Upgrade 建连; HTTP/2 可使用扩展 CONNECT, HTTP/3 也有相应扩展 CONNECT 机制. 具体客户端/服务端支持需要按协议版本和实现核验, 不能只描述 HTTP/1.1 Upgrade.
四, HTTPS 与 TLS (按证据准备)
HTTPS = HTTP + TLS/SSL, 解决三个问题:加密 (防窃听), 完整性 (防篡改), 身份认证 (防冒充).
TLS 握手: RSA 密钥交换 vs ECDHE
TLS 的核心思想是:先认证身份并协商出对称会话密钥, 后续用对称加密传 HTTP 数据. 对称加密快, 但首次密钥分发困难; 非对称 / 密钥交换负责解决首次协商与身份认证.
| 维度 | RSA 密钥交换 (TLS 1.2 旧式) | ECDHE 密钥交换 (现代主流) |
|---|---|---|
| 证书公钥用途 | 客户端用证书里的 RSA 公钥加密 pre-master secret | 证书用于验证服务端身份; 临时 ECDHE 公钥用于密钥交换 |
| 会话密钥来源 | pre-master secret + 双方随机数派生 | 双方临时椭圆曲线私钥 / 公钥计算共享秘密 + 随机数派生 |
| 前向保密 | 没有. 服务端私钥泄露后, 历史抓包可能被解密 | 有. 临时私钥握手后丢弃, 长期证书私钥泄露也难解历史流量 |
| 面试风险点 | 容易把 “证书公钥加密密钥” 当成所有 TLS 的固定流程 | 要说清 “证书认证身份, ECDHE 协商密钥” |
TLS 1.2 RSA 简化流程
- Client Hello: 客户端发支持的 TLS 版本, 加密套件, 随机数.
- Server Hello: 服务端选择套件, 返回随机数, 下发证书 (含公钥).
- 客户端验证证书 (CA 链, 域名, 有效期, 吊销状态等), 生成
pre-master secret, 用服务端 RSA 公钥加密后发送. - 服务端用私钥解密, 双方用
pre-master secret + client_random + server_random派生对称会话密钥. - 双方发送 Finished 校验握手完整性, 之后用对称加密通信.
TLS 1.2 ECDHE / TLS 1.3 直觉
- ECDHE: 服务端发送临时 ECDHE 公钥并用证书私钥签名, 客户端验证签名后也生成临时公钥; 双方各自用 “自己的临时私钥 + 对方临时公钥” 算出共享秘密. 网络上没有直接传输会话密钥.
- TLS 1.3: 移除静态 RSA 密钥交换等旧套件, 把常规握手压到 1-RTT: ClientHello 携带 key share, ServerHello 返回 key share 和证书相关消息, 双方很快得到密钥.
- TLS 1.3 握手序列 (1-RTT): ClientHello (含 key_share) → ServerHello (返回 key_share) 连同服务端证书 / CertificateVerify / Finished 一起到达 → 客户端验证证书并回 Finished → 应用数据. 相比 TLS 1.2 完整握手典型 2-RTT 后才能发应用数据, TLS 1.3 把证书与密钥协商结果合并在第一个 RTT 返回, 第二个 RTT 客户端即可发应用数据.
- 0-RTT: 客户端基于上次会话恢复提前发送早期数据, 降低重连延迟; 边界是早期数据可能被重放, 所以只适合幂等 GET / 查询类请求, 不适合支付, 下单, 转账, 登录态变更等非幂等操作.
面试一句话: 旧 RSA 像 “用证书公钥包住会话密钥”, 现代 ECDHE/TLS 1.3 更像 “证书负责证明你是谁, 临时密钥交换负责生成本次会话密钥”, 因此具备前向保密.
证书与信任链
- 证书由 CA 逐级签发, 设备内置根 CA. 验证时沿链校验到可信根.
- 为什么不能只用对称加密? 密钥分发难题: 首次怎么安全传密钥? 用非对称解决.
- 为什么不全用非对称? 慢. 所以只用它协商对称密钥.
证书校验至少包含: 验证链能否连到受信任锚点, 当前时间是否在有效期, 叶子证书 SAN 是否匹配请求主机名, 签名 / 用途和平台策略是否允许. 吊销检查, CT, 用户安装 CA 与网络安全配置的具体行为受 Android 版本, 网络栈和服务端能力影响. 不要为 “连通” 跳过 hostname verifier 或自定义信任所有证书; 这会把 HTTPS 降回可被中间人替换的明文等价风险.
主机名校验不能省: 即使证书链验证到受信根, 若叶子证书 SAN 不含所请求的 hostname, 也必须拒绝连接. 两步缺一不可: 链验证证明 “证书可信”, 主机名校验证明 “对方就是你要连的域名”, 跳过任一步都可能被中间人替换.
安全实战 (按可核验证据展开)
- 中间人攻击 (MITM): 攻击者伪造证书. 防御靠客户端严格校验证书.
- 证书锁定 (SSL Pinning): App 内置服务端证书/公钥指纹, 只信任它, 防抓包/MITM: 风控/金融 App 标配.
- 双向认证 (mTLS): 服务端也验证客户端证书.
- 抓包对抗: Charles/Fiddler 可通过安装根证书抓取 HTTPS; App 可用 Pinning + 检测代理对抗. 仅在有可披露, 可核验经历时, 将其作为实战案例说明.
五, TCP / UDP
TCP 三次握手
- Client → SYN(seq=x)
- Server → SYN+ACK(seq=y, ack=x+1)
- Client → ACK(ack=y+1) 为什么三次? 确认双方收发能力都正常; 两次无法确认客户端的接收能力, 且防止历史失效连接请求建立连接.
TCP 四次挥手
- Client → FIN 2. Server → ACK 3. Server → FIN 4. Client → ACK 为什么四次? 关闭是双向的, 服务端收到 FIN 后可能还有数据要发, 所以 ACK 和 FIN 分开. TIME_WAIT(2MSL): 主动关闭方等待, 确保最后 ACK 到达 + 让旧报文消散.
可靠性机制
序列号 + 确认应答, 超时重传, 滑动窗口 (流量控制), 拥塞控制.
拥塞控制: 看懂 cwnd / ssthresh 转换
- cwnd(congestion window): 发送端根据网络拥塞程度维护的拥塞窗口, 限制 “未确认在途数据量”.
- ssthresh(slow start threshold): 慢启动阈值, 决定从指数增长切到线性增长.
- 实际发送窗口通常取
min(cwnd, rwnd): 拥塞控制看网络承载能力, 流量控制看接收端缓存能力.
| 阶段 | 触发 / 进入条件 | cwnd 变化 | 退出条件 |
|---|---|---|---|
| 慢启动 | 连接刚建立或超时后重新探测 | 每个 RTT 近似翻倍 (指数增长) | cwnd >= ssthresh 转拥塞避免; 或丢包 |
| 拥塞避免 | cwnd 达到 ssthresh | 每个 RTT 近似 +1 MSS (线性增长) | 丢包 / 重复 ACK |
| 快重传 | 收到 3 个重复 ACK, 推测某段丢失但网络仍有流动 | 不等超时, 立即重传疑似丢失段 | 进入快恢复 |
| 快恢复 | 快重传之后 | 通常把 ssthresh 设为丢包前 cwnd 的一半, cwnd 降低后线性恢复 | 新 ACK 到达后回到拥塞避免 |
超时 vs 快重传的区别:
- 超时重传说明网络可能严重拥塞或 ACK 完全回不来, 处理更保守: 常见做法是
ssthresh = cwnd / 2, 然后cwnd回到很小值重新慢启动. - 3 个重复 ACK 说明后续包还能到达, 网络没有完全断流, 所以只减半窗口并快恢复, 比超时温和.
新连接/超时
↓ cwnd 指数增长
慢启动 ── cwnd >= ssthresh ──> 拥塞避免(线性增长)
│ │
└── 超时:ssthresh=cwnd/2,cwnd 重置 ─┘
│
└── 3 dup ACK:快重传 → 快恢复 → 拥塞避免
数字推演 (示意值): 现代 Linux 常见初始 cwnd 为 10 MSS (RFC 6928, 标注为常见默认而非协议强制). 慢启动每个 RTT 近似翻倍: 第 1 个 RTT 后约 20 MSS, 第 2 个约 40 MSS, 第 3 个约 80 MSS. 若 ssthresh 为 64 MSS (示意), 则约在第 3 个 RTT 触达阈值并切到拥塞避免的线性增长; 具体第几个 RTT 取决于 ssthresh 与丢包 / 显式拥塞反馈, 不能当固定值.
面试答题流: 先区分可靠性 (序号/ACK/重传) 与拥塞控制 (保护网络), 再解释 cwnd/ssthresh, 最后用 “超时更严重, 快重传更温和” 讲阶段转换.
TCP vs UDP
| TCP | UDP | |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠 | 可靠有序 | 不承诺可靠性 |
| 速度 | 慢 | 快 |
| 场景 | HTTP, 文件 | 音视频, DNS, QUIC |
高频面试题
Q1: HTTP 和 HTTPS 区别? HTTPS = HTTP + TLS, 提供加密, 完整性, 身份认证. HTTP 默认明文 80 端口, HTTPS 默认加密 443 端口, 需证书, 有握手开销但安全.
Q2: TLS 握手为什么用非对称 + 对称结合?
非对称 / 密钥交换解决首次协商和身份认证, 对称加密负责后续高性能传输. 旧 RSA 是客户端用证书公钥加密 pre-master secret; 现代 ECDHE/TLS 1.3 用临时密钥交换生成会话密钥, 证书主要证明服务端身份, 并提供前向保密.
Q3: TCP 为什么三次握手不是两次? 三次才能确认双方收发能力都正常, 并防止已失效的历史连接请求突然到达导致错误建连. 两次无法确认客户端接收能力.
Q4: TIME_WAIT 是什么? 为什么等 2MSL? 主动关闭方在四次挥手后进入 TIME_WAIT, 等 2 倍报文最大生存时间: 确保最后的 ACK 能到达对端 (否则对端重传 FIN), 并让本连接的旧报文在网络中消散.
Q5: HTTP/2 相比 1.1 的核心改进? 二进制分帧, 多路复用 (一个连接并发多请求, 解决应用层队头阻塞), 头部压缩 HPACK. 服务端推送是早期规范 (RFC 7540) 特性, 但 Chrome 等主流浏览器已移除支持, 不属当前核心特性.
Q6: 什么是 SSL Pinning? 为什么风控 App 要用? 客户端内置服务端证书或公钥指纹, 握手时只信任它而非系统 CA, 防止中间人用伪造证书抓包 / 篡改. 金融, 风控 App 用它对抗抓包和 MITM.
Q7: 输入 URL 到页面展示发生了什么? DNS 解析 → 建立 TCP 连接 (三次握手)→ TLS 握手 (HTTPS)→ 发 HTTP 请求 → 服务端响应 → 客户端解析渲染 → 关闭 / 复用连接.
进阶补充: DNS, QUIC, TCP 状态与 TLS 细节
DNS 解析链路
域名访问前通常经历缓存查询, 本地 DNS, 递归解析. 移动端排查网络慢时要区分 DNS, TCP, TLS, 请求, 响应各阶段.
HTTP/3 与 QUIC
HTTP/3 基于 QUIC, 而 QUIC 运行在 UDP 之上. QUIC 将 TLS 1.3 握手与自身连接建立结合, 并在用户态实现可靠传输, 流级有序交付和拥塞控制; UDP 仍只是底层数据报承载. 这避免了 TCP 丢包导致同一连接所有 HTTP/2 流停顿的问题, 也支持连接迁移. 面试不要把 “UDP 不保证可靠性” 延伸为 “HTTP/3 不可靠” 或 “UDP 不安全”.
TIME_WAIT / CLOSE_WAIT 排障
| 状态 | 常见原因 | 排查方向 |
|---|---|---|
| TIME_WAIT 多 | 主动关闭方等待旧包消失 | 连接复用, 服务端参数 |
| CLOSE_WAIT 多 | 本端未 close socket | 代码资源释放, 连接池 |
TLS 补充: SNI, ALPN, OCSP, 会话恢复
-
SNI: 客户端握手时告诉服务端目标域名.
-
ALPN: 协商 HTTP/1.1, HTTP/2 等应用协议.
-
OCSP: 检查证书吊销状态.
-
Session Ticket/PSK: 减少重复握手成本.
-
追问: HTTP/2 和 HTTP/3 都解决什么问题? HTTP/2 多路复用仍受 TCP 队头阻塞影响; HTTP/3 基于 QUIC 改善连接迁移和队头阻塞.