网络-00 一个请求的完整旅程
“在浏览器地址栏输入一个网址,按下回车,到页面显示出来,中间发生了什么?”
这是网络方向最经典的面试题。它的价值在于可以无限深挖——面试官可以从任意一个环节切进去追问细节,所以它既是一道题,也是整个知识体系的索引。
本文按时间顺序走完这条链路,每一步都指向系列里对应的深入篇章。这一篇是地图,不是细节。
1. 全流程概览
① URL 解析与预处理 ── 浏览器
② 检查缓存 ── 强缓存命中则直接结束
③ DNS 解析 → IP ── 网络-11
④ 建立 TCP 连接(三次握手) ── 网络-07
└ 需要 ARP 拿到网关 MAC ── 网络-02
⑤ TLS 握手(HTTPS) ── 网络-16 / 17
⑥ 发送 HTTP 请求 ── 网络-12
⑦ 服务端处理并返回响应 ──
⑧ 浏览器解析响应、渲染页面 ──
⑨ 连接的复用与关闭 ── 网络-07 / 15
2. ① URL 解析与预处理
浏览器拿到你输入的内容后,先要判断这到底是不是一个 URL:
- 输入
github.com→ 补全成https://github.com; - 输入
如何学习网络→ 判定为搜索词,拼接成默认搜索引擎的查询 URL; - 含非 ASCII 字符的域名(如
中文.com)→ 通过 Punycode 转换成xn--fiq228c.com,因为 DNS 只支持 ASCII; - 路径和查询参数里的特殊字符 → URL 编码(percent-encoding)。
然后拆解出 scheme、host、port、path、query(fragment 留在本地,不会发送)。URL 各部分的含义见 网络-12。
HSTS 检查:如果这个域名在浏览器的 HSTS 列表里(服务端此前通过 Strict-Transport-Security 头声明过"我只用 HTTPS"),浏览器会在发出请求前就把 http:// 强制改写为 https://。这一步发生在本地,不产生任何网络流量——它的意义是消灭"首次 HTTP 请求被劫持"的窗口。
3. ② 检查缓存
在动网络之前,先查本地有没有可用副本:
- 强缓存命中(
Cache-Control: max-age未过期)→ 直接使用,整个流程到此结束,一个字节都不发; - 需要协商 → 带上
If-None-Match/If-Modified-Since继续后面的流程,可能拿到304。
细节见 网络-13 HTTP 缓存。这是性能优化里收益最大的一环——命中强缓存的请求,后面 7 步全部省掉。
4. ③ DNS 解析
拿到域名,需要换成 IP。查找顺序是一条逐级放大的缓存链:
浏览器缓存 → 操作系统缓存 → hosts 文件 → 本地 DNS(递归)
└→ 根域 → TLD → 权威服务器(迭代)
绝大多数请求在前几层缓存就返回了。完整过程、递归与迭代的分工、TTL 的坑见 网络-11 DNS。
这一步的常见故障:DNS 解析慢是"网页打开慢"最容易被忽略的原因,因为它发生在任何业务代码之前。用 curl -w "%{time_namelookup}" 可以单独量出这段耗时(见 网络-12 的 curl 小节)。
5. ④ 建立 TCP 连接
有了 IP,接下来要建立连接。但在发出第一个 TCP 包之前,还有一个常被跳过的环节。
5.1 先要确定往哪台机器发
主机用子网掩码判断目标 IP 是否在同一网段(网络-03):
这里是那个高频考点:数据帧的目的 MAC 填的是网关的 MAC,而 IP 首部的目的 IP 始终是最终目标。IP 负责端到端寻址(全程不变),MAC 负责逐跳转发(每经过一个路由器就重写)。
5.2 三次握手
客户端 ──── SYN (seq=x) ────────────────► 服务端
客户端 ◄─── SYN+ACK (seq=y, ack=x+1) ─── 服务端
客户端 ──── ACK (ack=y+1) ─────────────► 服务端
连接建立,耗时 1 RTT
握手由内核自动完成,完成后连接进入 accept 队列等应用 accept() 取走。为什么是三次、队列满了会怎样,见 网络-07 TCP 连接管理。
这一步的耗时是 1 个 RTT。跨国访问时 RTT 可能 200ms 以上,这也是后面 HTTP/3 要把握手压缩到 0-RTT 的动力。
6. ⑤ TLS 握手(HTTPS)
如果是 HTTPS,TCP 建立后还要再握一次手,协商出加密密钥。
| 协议版本 | 额外耗时 |
|---|---|
| TLS 1.2 | 2 RTT |
| TLS 1.3 | 1 RTT,会话恢复可 0-RTT |
核心步骤:交换随机数 → 验证证书(沿证书链一直校验到操作系统内置的根证书)→ 协商出会话密钥 → 之后用对称加密通信。
证书验证、CA 体系、中间人原理见 网络-16 HTTPS 与 TLS;TLS 1.3 的改进和前向保密见 网络-17。
到这里累计延迟:DNS + TCP 1 RTT + TLS 1~2 RTT。这就是为什么"首次访问慢,后面快"——后续请求可以复用连接,把这些成本全部省掉。
7. ⑥ 发送 HTTP 请求
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 ...
Accept: text/html,application/xhtml+xml
Accept-Encoding: gzip, deflate, br
Cookie: session_id=abc123
几个关键点:
Host头是必需的——服务器靠它区分同一 IP 上的多个站点(网络-12);- Cookie 自动携带——浏览器根据域名匹配自动附上,这既是便利也是 CSRF 的根源(网络-14);
- 请求要经过分层封装:HTTP 报文 → TCP 段 → IP 包 → 以太网帧(网络-01)。
8. ⑦ 服务端处理
请求到达服务端后的典型路径:
负载均衡 (LB) → 反向代理 (Nginx) → 应用服务 → 缓存/数据库
这一段是后端的领域,网络视角需要关注的是它可能返回什么:
200正常,304缓存有效,301/302重定向(浏览器会回到第 ① 步重新走一遍全流程);502后端挂了、504后端太慢、503主动拒绝——这三个的区分是排障基本功,见 网络-12。
注意 3xx 重定向意味着整条链路重来一次。一次多余的重定向 = 又一轮 DNS + TCP + TLS,这是为什么要尽量减少重定向跳数。
9. ⑧ 浏览器渲染
拿到 HTML 后,浏览器的工作大致是:
HTML → 解析 → DOM 树 ┐
├→ 渲染树 → 布局(Layout) → 绘制(Paint) → 合成(Composite)
CSS → 解析 → CSSOM ┘
从网络角度需要理解的是资源加载的阻塞关系:
- CSS 阻塞渲染——不下载完不会绘制(避免闪烁未样式化的内容);
- JS 默认阻塞解析——因为脚本可能修改 DOM,浏览器不敢继续往下解析。用
defer/async可以解除阻塞; - 解析过程中遇到
<img>、<script>、<link>会发起新的请求——每一个都要重走前面的流程(但通常能复用连接)。
所以一个页面的实际网络行为是几十到上百个请求,而不是一个。这正是 HTTP/2 多路复用要解决的问题(网络-15)。
10. ⑨ 连接的复用与关闭
HTTP/1.1 默认长连接(Connection: keep-alive),一条 TCP 连接可以连续发多个请求,省掉重复握手。但它有队头阻塞问题——响应必须按请求顺序返回。
| 版本 | 并发方式 | 遗留问题 |
|---|---|---|
| HTTP/1.1 | 多条 TCP 连接(每域名 6 条) | HTTP 层队头阻塞 |
| HTTP/2 | 单连接多路复用 | TCP 层队头阻塞 |
| HTTP/3 | QUIC 流级独立 | UDP 可能被屏蔽 |
最终连接通过四次挥手关闭,主动关闭方会进入 TIME_WAIT 等待 2MSL(网络-07)。
11. 一张图串起所有层
你的输入
│
▼
┌──────────────────────────────────────────────┐
│ 应用层 URL解析 → 缓存 → DNS → HTTP 报文 │ 网络-11,12,13,14,15
├──────────────────────────────────────────────┤
│ (TLS) 证书验证 → 密钥协商 → 加密 │ 网络-16,17
├──────────────────────────────────────────────┤
│ 传输层 三次握手 → 分段 → 可靠传输 → 流控 │ 网络-06,07,08,09
├──────────────────────────────────────────────┤
│ 网络层 IP 寻址 → 路由 → 分片 │ 网络-03,04,05
├──────────────────────────────────────────────┤
│ 链路层 ARP → 封装成帧 → MAC 逐跳转发 │ 网络-02
└──────────────────────────────────────────────┘
│
▼
物理链路
12. 延迟都花在哪了
把这条链路按耗时拆开,是性能优化的起点:
| 阶段 | 典型耗时 | 优化手段 |
|---|---|---|
| DNS 解析 | 0~200ms | DNS 预解析(dns-prefetch)、长 TTL、本地缓存 |
| TCP 握手 | 1 RTT | 长连接复用、连接预热(preconnect) |
| TLS 握手 | 1~2 RTT | TLS 1.3、会话恢复、OCSP Stapling |
| 请求响应 | 取决于后端 | CDN、缓存、后端优化 |
| 内容传输 | 取决于体积 | gzip/br 压缩、图片优化 |
| 渲染 | — | 关键 CSS 内联、JS 异步加载 |
用 curl 可以直接量出每一段:
curl -s -o /dev/null -w "DNS:%{time_namelookup} 连接:%{time_connect} TLS:%{time_appconnect} 首字节:%{time_starttransfer} 总计:%{time_total}\n" https://example.com
三个最有效的优化,恰好对应三个不同层次:
- 命中强缓存 —— 直接跳过全部 9 步(第 ② 步);
- 复用连接 —— 跳过 TCP + TLS 握手(第 ④⑤ 步);
- 就近接入(CDN) —— 缩短 RTT 本身,等比例改善所有需要往返的环节。
13. 全系列考点地图
按主题分组,链接指向对应篇章。各篇末尾有折叠自测题,可以对照复习。
13.1 分层与链路层
13.2 网络层
- IP 地址结构、CIDR、IPv4/IPv6 首部、NDP 与 Go 双栈监听 → 网络-03
- MTU 与 MSS 的区别、PMTUD、IPv6 分片与隧道开销 → 网络-04
- 网关的 MAC 地址(高频)、最长前缀匹配、NAT 与 conntrack → 网络-05
13.3 传输层
- UDP 特点、TCP 与 UDP 对比与选型、UDP 上的可靠传输 → 网络-06
- 为什么三次握手 / 四次挥手、TCP 状态机、TIME_WAIT 与 2MSL、半连接与全连接队列、SYN Flood → 网络-07
- TCP 首部、RTO 与指数退避、SACK、粘包与拆包、Go 字节流处理 → 网络-08
- 滑动窗口、流量控制 vs 拥塞控制、BDP、CUBIC/BBR 与 ECN → 网络-09
- CLOSE_WAIT / TIME_WAIT 堆积的定位与处置、keepalive、队列与缓冲区参数、BBR → 网络-10
13.4 应用层
- 域名分级、递归 vs 迭代查询、记录类型、TTL、DNS 何时用 TCP、DoH/DoT → 网络-11
- HTTP 报文结构、幂等性、GET vs POST、状态码体系(301/302/307、401/403、502/503/504) → 网络-12
- 强缓存 vs 协商缓存、
no-cachevsno-store、ETag vs Last-Modified → 网络-13 - Cookie 属性、Session vs Token vs JWT、同源策略、CORS 预检、CSRF → 网络-14
- HTTP 版本演进、队头阻塞(HTTP 层 vs TCP 层)、多路复用、HPACK → 网络-15
13.5 安全与新协议
- 对称与非对称加密、数字证书与 CA 体系、TLS 握手、抓包与反抓包 → 网络-16
- TLS 1.3 的改进、1-RTT / 0-RTT、前向保密 → 网络-17
- QUIC 为什么基于 UDP、连接迁移、QPACK、HTTP/3 部署 → 网络-18
13.6 编程与实践
- WebSocket 握手与帧格式、与 SSE / 轮询的对比、心跳保活、关闭握手 → 网络-19
- Socket 不是协议、API 调用流程与 TCP 状态的对应、内核缓冲区、Go netpoller → 网络-20
- tcpdump 过滤语法、Wireshark 读包与统计、从包里认出握手/重传/RST、TLS 解密 → 网络-21
- 按症状索引的排查手册:连不上 / 慢 / 中断 / DNS 异常 / MTU 黑洞、工具速查表 → 网络-22
- Go net/http 生产实践:连接池、分层超时、安全重试、服务端防护与优雅退出 → 网络-23
13.7 高频题速查
如果时间紧,这些是出现频率最高的:
| 题目 | 篇章 |
|---|---|
| 从输入 URL 到页面显示发生了什么 | 本文 |
| 为什么三次握手、四次挥手 | 07 |
| TIME_WAIT 为什么等 2MSL | 07 |
| TCP 如何保证可靠传输 | 08 |
| 流量控制和拥塞控制的区别 | 09 |
| TCP 和 UDP 的区别与选型 | 06 |
| 大量 CLOSE_WAIT / TIME_WAIT 怎么排查 | 10 |
| GET 和 POST 的区别 | 12 |
| 强缓存与协商缓存 | 13 |
| 跨域是什么,CORS 怎么解决 | 14 |
| HTTPS 如何保证安全 | 16 |
| HTTP/2 解决了什么,还剩什么问题 | 15 / 18 |
| Go HTTP 客户端为什么必须复用 Transport | 23 |
| 接口变慢/连不上怎么排查 | 22 |
14. 本章面试题
从输入 URL 到页面显示,完整讲一遍
九个阶段:
- URL 解析 —— 判断是否为 URL、Punycode 转换、URL 编码、HSTS 检查(本地把 http 改写为 https);
- 检查缓存 —— 强缓存命中则直接结束,一个字节都不发;
- DNS 解析 —— 浏览器缓存 → 系统缓存 → hosts → 本地 DNS(递归)→ 根/TLD/权威(迭代);
- 建立 TCP 连接 —— 先用子网掩码判断是否同网段,通过 ARP 拿到目标或网关的 MAC,然后三次握手(1 RTT);
- TLS 握手 —— 验证证书、协商密钥(TLS 1.2 为 2 RTT,1.3 为 1 RTT);
- 发送 HTTP 请求 —— 带
Host、Cookie 等头部; - 服务端处理 —— LB → Nginx → 应用 → 数据库,返回状态码;
- 浏览器渲染 —— HTML/CSS 解析成 DOM/CSSOM → 渲染树 → 布局 → 绘制;
- 连接复用与关闭 —— keep-alive 复用,最后四次挥手。
加分点:说清 ARP 那一步(很多人漏)、指出 3xx 重定向会让整条链路重来、说出每步的 RTT 成本。
这个过程中的延迟主要在哪几段?怎么优化?
成本拆解:DNS(0~200ms)+ TCP 握手(1 RTT)+ TLS 握手(1~2 RTT)+ 服务端处理 + 内容传输。
三个最有效的优化,对应三个层次:
- 命中强缓存 → 跳过全部流程(收益最大);
- 复用长连接 → 跳过 TCP + TLS 握手;
- CDN 就近接入 → 缩短 RTT 本身,等比例改善所有往返环节。
其他:TLS 1.3 省一个 RTT、dns-prefetch / preconnect 预热、gzip/br 压缩、减少重定向跳数。
用 curl -w "%{time_namelookup} %{time_connect} %{time_appconnect} %{time_starttransfer}" 可以直接量出每段耗时。
为什么"首次访问慢,后续访问快"?
首次访问要付全部固定成本:DNS 解析 + TCP 三次握手(1 RTT)+ TLS 握手(1~2 RTT)。
后续请求:
- DNS 结果已被缓存;
- TCP 连接通过 keep-alive 复用,不再握手;
- TLS 可用会话恢复(TLS 1.3 甚至 0-RTT);
- 静态资源可能直接命中强缓存,完全不发请求。
所以首次的延迟主要是协议握手成本,而非带宽不足。
请求发出前,浏览器怎么知道往哪台机器发数据帧?
分两步,这是最容易漏答的环节:
- 用子网掩码与目标 IP 做与运算,判断是否同网段;
- 同网段 → ARP 拿到目标主机 MAC;不同网段 → ARP 拿到默认网关的 MAC。
关键结论:帧首部的目的 MAC 是网关的 MAC,而 IP 首部的目的 IP 始终是最终目标。IP 端到端寻址(全程不变),MAC 逐跳转发(每跳重写)。
xingliuhua