在地址栏敲下网址、按下回车,到页面完整出现在屏幕上,中间要经过 DNS 解析、TCP 建连、TLS 握手、HTTP 交互、服务器处理和浏览器渲染等一系列环节。这道题是面试常客,因为它能把从应用层到链路层的核心协议一次性串起来。

它解决什么问题

单独背协议容易断片:知道 TCP 有三次握手,却不知道它在访问网页时发生在哪一步;知道 DNS 是域名解析,却说不清谁来发查询、问谁要答案。”输入 URL 后发生了什么”给出的不是新知识点,而是一张地图——把每个协议放回它在链路中的位置,看清每一步”做什么、为什么需要它、和上下层怎么交接”。理解了这条主线,页面打开慢、HTTPS 配置错误、跨域失败这类实际问题,才有分段定位的框架。

核心原理:全流程鸟瞰

先把主线画出来,再逐段展开:

输入 URL → 查浏览器缓存 → DNS 解析(域名→IP) → TCP 三次握手
→ TLS 握手(仅 HTTPS) → 发送 HTTP 请求 → 服务器处理并响应
→ 浏览器解析渲染(DOM/CSSOM/渲染树/布局/绘制/合成) → 连接复用或关闭

注意各层协议的衔接点:DNS 工作在应用层,它的产出是一个 IP 地址,这是向传输层交代”连谁”;TCP 在传输层,为 HTTP 提供可靠有序的字节流通道;TLS 插在 TCP 与 HTTP 之间,把明文通道升级为加密通道;HTTP 本身只定义报文格式,真正的传输全部委托给下层。数据在发送端自应用层向下逐层加头封装,到了对端再自链路层向上逐层解封装,每一层只读写自己的头部。

深入机制

DNS 解析:递归与迭代接力

  • 做什么:把 www.example.com 这样的人类可读域名,翻译成网络层真正用来寻址的 IP 地址。
  • 为什么需要:路由器只认 IP,域名纯粹为记忆而存在,必须有一座”翻译机”。
  • 怎么查:先查浏览器 DNS 缓存,再查操作系统缓存,再看本地 hosts 文件;都没命中,才发起真正的 DNS 查询。客户端向本地 DNS 服务器(运营商或公共 DNS 如 114.114.114.114)发起的是递归查询——“你必须给我最终答案”;本地 DNS 服务器缓存未命中时,自己出面做迭代查询:先问根服务器拿到 .com 顶级域服务器地址,再问顶级域服务器拿到权威服务器地址,最后问权威服务器拿到目标 IP。结果逐级缓存后返回给浏览器。绝大多数查询走 UDP 53 端口,一次请求一次响应,省去握手开销。

TCP 三次握手

  • 做什么:在双方之间建立一条全双工连接,同步各自的初始序列号。
  • 为什么需要:HTTP 依赖可靠、有序的字节流,必须先确认”你收得到我、我也收得到你”再传数据。握手过程是:客户端发 SYN(seq=x);服务端回 SYN+ACK(seq=y, ack=x+1);客户端再回 ACK(ack=y+1),双方进入 ESTABLISHED。两次握手不够,因为服务端发出 SYN+ACK 后,无法确认客户端是否收到——第三次握手补上的正是服务端对”客户端接收能力”的确认。

TLS 握手(HTTPS 才有)

  • 做什么:验证服务器身份,并在公开信道上协商出只有双方知道的会话密钥。
  • 为什么需要:HTTP 是明文协议,链路上任何中间节点都能窃听、篡改、冒充,TLS 在 TCP 之上、HTTP 之下补上机密性、完整性与身份认证。
  • 怎么握:以 TLS 1.2 + ECDHE 为例,客户端发 ClientHello(版本、密码套件、随机数);服务端回 ServerHello、证书链和自己的临时 ECDHE 公钥(用证书私钥签名);客户端用 CA 信任链验证证书后,也生成临时 ECDHE 公钥发过去,双方各自算出同一个预主密钥,再派生会话密钥。此后 HTTP 报文全部用这把对称密钥加密。TLS 1.3 把完整握手压缩到 1-RTT,并砍掉了静态 RSA 密钥交换。

HTTP 请求与服务器处理

  • 做什么:浏览器把”我要什么资源”按固定格式写成报文发出去,服务器处理后把结果写回来。
  • 一个典型的 HTTP/1.1 请求:
GET /index.html HTTP/1.1
Host: www.example.com
Accept-Encoding: gzip, br
Connection: keep-alive
Cookie: session_id=abc123
  • 其中 Host 是 HTTP/1.1 的强制头,因为同一 IP 上可能虚拟托管多个站点,仅靠 IP 无法区分。服务器一侧的处理链路是:Nginx 等 Web 服务器接收并解析报文,按路径把请求分发给后端应用,应用执行业务逻辑(查缓存、读数据库、调下游服务),最后把结果封装成响应报文——状态行(如 HTTP/1.1 200 OK)、响应头(Content-Type、Cache-Control、Set-Cookie)加响应体(HTML、JSON 或二进制)。

浏览器解析渲染

  • 做什么:把拿到的 HTML 文本变成屏幕上的像素。
  • 为什么讲它:页面”能看”不代表”加载完”,渲染管线的阻塞行为直接决定首屏快慢。流程是:解析 HTML 标签构建 DOM 树;下载并解析 CSS 构建 CSSOM 树;两者合并成只含可见节点的渲染树(display: none 的不在内);再做布局算出每个节点的位置大小,绘制成像素,最后合成各图层输出画面。CSS 会阻塞渲染(样式没算完不能画),JS 默认阻塞 HTML 解析(脚本可能改 DOM),所以工程上才有 CSS 放头部、JS 加 defer 的约定。子资源加载时,HTTP/1.1 浏览器对单域名最多开 6 条并发连接,HTTP/2 则在一条连接上多路复用。

工程实践

  • 分段定位慢:页面慢先别猜,按 DNS 耗时、TCP 建连、TLS 握手、首字节时间(TTFB)、资源下载、渲染六段分别取证。浏览器 DevTools 的 Network 面板天然按这个分段展示瀑布图。
  • 缓存优先:解析 URL 后浏览器先查 HTTP 缓存——Cache-Control: max-age 未过期直接命中强缓存,完全不走网络;否则带 If-None-Match 发协商请求,服务端返 304 就用本地副本。
  • 连接复用:HTTP/1.1 默认 keep-alive,一条连接连续传多个请求,省去重复握手;HTTP/2 多路复用解决了应用层队头阻塞,但 TCP 层丢包仍会卡住所有流,这是 HTTP/3 转向 QUIC(基于 UDP)的动机。

对比与权衡

阶段 核心协议/机制 这层出问题时的典型现象 常用排查手段
DNS 解析 DNS(UDP/53) 首次访问慢、域名打不开 dig/nslookup
TCP 建连 TCP 三次握手 连接超时、SYN 重传 ping、telnet ip 80、抓包
TLS 握手 TLS 1.2/1.3 证书告警、握手失败 openssl s_client
HTTP 交互 HTTP/1.1、2、3 状态码异常、TTFB 高 DevTools、服务端日志
渲染 DOM/CSSOM/合成 白屏久、卡顿 Performance 面板

面试视角

DNS 的递归查询和迭代查询有什么区别?

看”谁跑腿”。客户端问本地 DNS 服务器是递归:只问一次,对方必须返回最终结果。本地 DNS 服务器自己查根、顶级域、权威服务器是迭代:每次只拿到”下一个该问谁”,多跑几趟。实际链路是两者接力。

TCP 握手为什么是三次,两次行不行?

不行。握手的本质是双方都要确认对方的收发能力。两次握手后,服务端无法确认客户端收到了自己的 SYN+ACK,若这个确认丢失,客户端认为连接没建立,服务端却可能空等甚至白白分配资源。

HTTPS 比 HTTP 在流程上多了什么?

TCP 建连之后、HTTP 请求之前多了 TLS 握手:验证证书确认服务器身份,用 ECDHE 协商会话密钥。代价是 1-2 个 RTT 的额外延迟,可用会话复用和 TLS 1.3 缓解。

为什么建议 CSS 放头部、JS 放底部或加 defer?

CSSOM 没建好就无法构建渲染树,CSS 加载慢会直接推迟首屏,所以要尽早加载;JS 默认阻塞 HTML 解析且可能读写 DOM,放底部或加 defer 可以让解析先跑完,避免白屏。

小结

  • 主线流程:解析 URL 与查缓存 → DNS 解析 → TCP 握手 → TLS 握手(HTTPS)→ HTTP 请求响应 → 浏览器渲染 → 连接复用或关闭。
  • DNS 是递归加迭代的接力:客户端递归问本地 DNS,本地 DNS 迭代问根、顶级域、权威服务器。
  • TLS 插在 TCP 与 HTTP 之间,负责身份认证与密钥协商;HTTP 只管报文格式,可靠传输和安全全部委托给下层。
  • 浏览器渲染走 DOM → CSSOM → 渲染树 → 布局 → 绘制 → 合成管线,CSS 阻塞渲染、JS 阻塞解析是性能优化的出发点。
  • 遇到”页面慢”,按 DNS、TCP、TLS、TTFB、下载、渲染六段定位,比凭感觉猜有效得多。