网页都知道要双向握手才加载!从 URL 到页面渲染,单向喜欢连 DNS 都解析不通

0 阅读14分钟

前言

有的人像 UDP 情绪来的时候一句话发出去,回不回来无所谓,至少那一刻是真实的。有的人像 TCP 每一句话都小心翼翼的等待回应,每一步靠近都要对方确认,缺少哪一次握手就不敢继续。而她,她既不是 UDP 也不是 TCP,她只是对你不想通信。真正的残酷不是丢包,不是延迟,不是超时,而且你始终在发送,她一直在沉默,你把自己当成 TCP 一样努力维持连接,一次次,你好,在吗,怎么不回消息,像三次握手一样想建立稳定的关系,但她从未回过你的 SYN,连第一步都没想过要和你完成,所以别再自我欺骗了,不是你没发好,不是你格式错了,不是你不够温柔细心坚持,是她压根就不想接受你的数据流,她不主动,不是害羞,不是忙,不是不知道说什么,而是她没有把你加入她的连接表里,你再怎么重传,重连,等待确认,都改变不了一件事,她不想与你通信,这才是全部真相

敲下 URL 后,网页到底偷偷做了什么?

对着浏览器地址栏敲下https://www.baidu.com,按下回车的瞬间,短短几秒百度首页就出现在眼前,这背后到底藏着怎样的 “神仙操作”?其实从输入网址到页面渲染,就像一场精心编排的 网络接力赛,今天我们一起去现场看看。

image.png

一、第一步:DNS 解析,给网址找个 “身份证”

想要访问百度,浏览器首先得知道:www.baidu.com这个好记的网址,对应的真实 “网络地址”(IP 地址)是什么?这就像你想找朋友玩,光知道名字不够,还得知道他家的具体住址,DNS 解析干的就是这个 “查住址” 的活儿。

浏览器会先向本地域名服务器发起查询,如果本地服务器有缓存,直接就能拿到网页;要是本地没有,这场 “查地址” 的旅程 就会继续:

  • 本地服务器先问根域名服务器
  • 再问顶级域名服务器
  • 最后找到百度的目标域名服务器,直到拿到对应的 IP 地址

拿到 IP 后,服务器还会把这个地址存进 DNS 缓存,下次再访问百度,就能直接 “抄近路”,不用再反复查询了。这就是为什么我们第一次打开百度可能等几秒,但后面几乎不需要等待。

image.png

简单说,DNS 解析就是给网址匹配唯一 IP 的过程,有了这个 IP,浏览器才能精准找到百度的服务器,开启后续的通信。

二、第二步:TCP 三次握手,和服务器 “握个手交个朋友”

拿到了百度服务器的 IP 地址,浏览器还不能直接发请求,因为互联网上的数据传输,全靠 TCP 协议 “保驾护航”,而 TCP 协议要求通信双方必须先建立连接,这就是大名鼎鼎的 三次握手

  1. 浏览器(客户端)先给服务器发一个 SYN 包,相当于说:“嗨,我想和你建立连接,行不行?”,随后客户端进入等待状态
  2. 服务器收到后,回传一个 SYN-ACK 包,意思是:“我收到啦,我同意连接,你确认一下?”,服务器也进入等待状态
  3. 浏览器收到服务器的回应,再发一个 ACK 包:“收到你的同意啦,咱们连接建立成功!”,至此双方都进入正常通信状态

image.png

面试考点:为什么不是两次/四次,而是三次握手呢?

  • 两次握手只能确保服务器收到了客户端的请求,但客户端没法确认服务器是否真的准备好,很容易出现连接失败、数据传输出错的问题,会产生失效的半连接,浪费资源
  • 四次握手步骤冗余,没必要
  • 三次握手刚好能 双向验证收发能力,保证双方都正常可用,是建立可靠连接的最少必要次数

三、第三步:HTTP/HTTPS 通信,向服务器 “要资源”

连接建立成功后,就到了核心的 通信环节,浏览器要向百度服务器发送 “请求”,要到百度首页的相关资源,这一步的 “沟通语言” 就是 HTTP/HTTPS 协议。

1. HTTP 协议:网络通信的 “通用普通话”

HTTP 协议是基于 TCP 的应用层协议,就像客户端和服务器之间约定好的普通话,定义了双方怎么说话、怎么传数据。它的发展也经历了好几个版本,每一次升级都在解决前一个版本的痛点,堪称 “持续优化的典范”,这边我给到一个夯 😄。

image.png

HTTP/0.9:最原始的版本,主打一个 “简单”,只能传输小小的 HTML 文件,没有请求头、响应头,就像两个人说话只说核心内容,没有任何客套话
HTTP/1.0:随着图片、视频、JS 文件等资源需要传输,这个版本新增了请求头、响应头,还加入了状态码、缓存机制,就像说话时加上了 “敬语” 和 “补充说明”,能传递更多信息,满足多种文件的传输需求
HTTP/1.1:实现了持久连接,一次 TCP 连接能传多个请求和响应,不用每次请求都重新握手,效率大大提升,还加入了 host 字段指定目标主机,但也存在队头阻塞的问题,一个请求卡壳,后面的请求都得等着
HTTP/2.0:针对 1.1 的痛点升级,只保留一个 TCP 连接,把多个请求切成小片段,还能给片段打加急标签,服务器可以优先处理重要请求,解决了队头阻塞,还加入了头部压缩,减少数据传输量,让通信更高效
HTTP/3.0:发现 TCP 协议本身还是有队头阻塞问题,干脆 “换了赛道”,基于 UDP 协议打造了 QUIC 协议,既保留了 TCP 的可靠传输、流量控制优势,又解决了队头阻塞,还实现了 TLS 加密、快速握手,堪称目前最完美的版本。

2. HTTPS:给 HTTP 加个 “加密保险箱”

我们平时访问的百度是HTTPS开头,而非HTTP,多出来的这个 S,就是 SSL/TLS 加密协议,相当于给 HTTP 通信加了一个 “加密保险箱”,防止数据在传输过程中被窃取、篡改。

image.png

它的加密方式很巧妙,结合了对称加密和非对称加密:

  • 客户端先生成一个密钥,服务器生成一对公钥和私钥
  • 服务器把公钥发给客户端,客户端用公钥给密钥加密后传给服务器,
  • 只有服务器的私钥能解开这个加密的密钥
  • 之后双方就用这个密钥进行对称加密通信,既保证了加密的安全性,又兼顾了传输的效率

image.png

简单来说,浏览器通过 HTTP/HTTPS 协议向服务器发送请求,告诉服务器:“我需要百度首页的 HTML、CSS、JS 等资源”,服务器收到请求后,会根据请求内容准备好对应的资源。

四、第四步:服务器响应,把资源 “送过来”

百度服务器收到浏览器的 合法请求 后,会立刻开始 “备货”,把首页的 HTML 文件、图片资源、样式文件、脚本文件等整理好,通过已经建立的 TCP 连接,再借助 HTTP/HTTPS 协议,将这些资源一步步传 输回浏览器

在传输过程中,TCP 协议会全程保驾护航

  • 把大的资源分成一个个小数据包,给每个数据包标上序列号,确保数据有序传输
  • 接收端收到数据包后,会发回确认号,要是某个数据包丢失,发送端会重新传输,这就是 TCP 的可靠传输
  • 如果是 HTTP/2.0 或 3.0,还会通过分块传输、多路复用等方式,让资源传输更快、更顺畅

五、第五步:浏览器渲染,让网页 “活起来”

当浏览器拿到服务器传来的所有资源后,就到了最后一步 —— 页面渲染,这也是让百度首页从一堆代码变成我们看到的精美页面的关键。

1. 解析 HTML,构建 DOM 树

浏览器会逐行读取 HTML 代码,将每个标签、属性、文本转换成 DOM(文档对象模型)节点,最终形成一棵层级分明的 DOM 树。DOM 树是页面结构的 “骨架”,记录了所有元素的层级关系和基本信息。

<!-- 原始HTML -->
<!DOCTYPE html>
<html>
  <head>
    <title>测试页面</title>
  </head>
  <body>
    <div class="box">
      <p>Hello DOM!</p>
    </div>
  </body>
</html>

对应的简化 DOM 树结构:

html
├─ head
│  └─ title (文本:测试页面)
└─ body
   └─ div (class="box")
      └─ p (文本:Hello DOM!)

你也可以在浏览器控制台输入 documentconsole.dir(document),直接查看当前页面的完整 DOM 树结构。

image.png

2. 解析 CSS,构建 CSSOM 树

浏览器读取所有 CSS(内联、内嵌、外部 CSS),解析样式规则,生成 CSSOM(CSS 对象模型)树。CSSOM 树是样式的 “规则集”,记录了每个元素该应用的样式(如颜色、大小、位置等),且会考虑样式的优先级(如行内样式 > ID 选择器 > 类选择器)。

/* 原始CSS */
.box {
  width: 200px;
  background: #f0f0f0;
}
.box p {
  color: red;
  font-size: 16px;
}

对应的简化 CSSOM 树结构:

.box
├─ width: 200px
├─ background: #f0f0f0
└─ .box p
   ├─ color: red
   └─ font-size: 16px

image.png

:CSSOM 树会自动处理样式继承和优先级,比如<p>会继承<div>的部分样式(如字体),但优先应用自身的样式规则

3. 合并 DOM 树和 CSSOM 树,生成渲染树

浏览器会将 DOM 树CSSOM 树 合并,只保留 “需要显示的元素”(如<body>内的可见元素,排除<head>display: none的元素),并为每个元素绑定对应的样式规则,最终形成渲染树。渲染树是 “带样式的骨架”,既包含结构,又包含样式。

4. 布局(Layout / 回流)

基于渲染树,浏览器计算每个元素的精确位置(如 top、left)、大小(width、height)、行高、间距等,这个过程也叫 “回流”。比如计算.box的宽 200px,<p>的字体大小 16px,以及它们在页面中的坐标。

5. 绘制(Paint)

浏览器根据布局结果,将元素的视觉属性(颜色、背景、边框、阴影、图片等)逐个画在屏幕上,最终形成我们看到的可视化页面。

image.png

6. 执行 JS 脚本(穿插在渲染过程中)

JS 脚本的执行会穿插在上述步骤中:

  • 如果 JS 写在<head>且没有defer/async,浏览器会暂停 HTML 解析,先执行 JS(此时 DOM 树可能未构建完成)
  • 如果 JS 操作 DOM/CSS(如document.querySelector('.box').style.color = 'blue'),会触发 DOM/CSSOM 更新,甚至重新布局 / 绘制,这也是为什么频繁操作 DOM 会影响页面性能
// 等待DOM加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
  // 获取DOM节点
  const pTag = document.querySelector('.box p');
  
  // 修改文本(更新DOM)
  pTag.textContent = 'Hello JS + DOM!';
  
  // 修改样式(更新CSSOM,可能触发重绘)
  pTag.style.color = 'blue';
});

六、通信结束:TCP 四次挥手,“友好告别”

如果我们关闭百度页面,客户端和服务器的通信就结束了,这时候 TCP 协议会进行四次挥手,主打一个 “好聚好散,清理资源”

第一步:客户端发起 “分手请求”(FIN 包)

客户端主动发送带有FIN(结束)标志位的数据包,告诉服务器:“我这边已经没有数据要发给你了,准备断开连接啦!”,发送完成后,客户端进入FIN_WAIT_1(等待结束)状态。

第二步:服务器 “收到通知,先回应”(ACK 包)

服务器收到客户端的 FIN 包后,立即回传带有ACK(确认)标志位的数据包,意思是:“我收到你的断开请求了,你先等一等,我这边可能还有剩余数据要处理 / 传输!”,服务器此时进入CLOSE_WAIT(关闭等待)状态,客户端收到 ACK 包后,进入FIN_WAIT_2状态,等待服务器的最终通知。

第三步:服务器 “处理完收尾工作,正式提分手”(FIN 包)

服务器把剩余未传输完的数据全部发送给客户端,确认自身无数据需要传输后,向客户端发送带有FIN标志位的数据包,告知:“我这边数据也都发完了,我也准备好断开连接了!”,发送完成后,服务器进入LAST_ACK(最后确认)状态。

第四步:客户端 “确认收尾,正式断开”(ACK 包)

客户端收到服务器的 FIN 包后,回传带有ACK标志位的数据包,告诉服务器:“收到你的断开确认了,咱们的连接可以彻底断了!”,客户端发送完 ACK 包后会短暂进入TIME_WAIT(时间等待)状态(防止延迟的数据包干扰新连接),服务器收到 ACK 包后立即进入CLOSED(已关闭)状态,释放占用的网络资源;客户端等待一段时间后也进入CLOSED状态,至此 TCP 连接完全断开。

image.png

面试考点:为什么挥手需要四次?

TCP 是全双工通信(简单说就是客户端和服务器能同时向对方发数据),断开连接需要分别关闭 “客户端→服务器” 和 “服务器→客户端” 两个方向的通信。

如果只做三次挥手,服务器就得在收到 FIN 包后,立刻同时发 ACK(确认)和 FIN(关闭)包,但这会导致服务器来不及传输剩余数据,大概率造成数据丢失 —— 毕竟服务器收到断开请求时,可能还囤着要发给客户端的 “尾款数据”,必须先处理完,才能真正说 “分手”

总结:面试该怎么简要概括?

DNS 解析:浏览器通过 DNS 服务器(本地→根→顶级→目标)将域名(如www.baidu.com)解析为对应 IP 地址,拿到服务器的 “网络地址”

TCP 连接:客户端与服务器通过 TCP 三次握手建立可靠连接,确保双向通信的基础

HTTP/HTTPS 请求:浏览器基于 TCP 连接,通过 HTTP/HTTPS 协议向服务器发送资源请求(HTTPS 额外通过 SSL/TLS 加密保障安全)

服务器响应:服务器处理请求后,将 HTML/CSS/JS 等资源通过 TCP 连接回传给浏览器

页面渲染:浏览器先解析 HTML 生成 DOM 树、解析 CSS 生成 CSSOM 树,合并为渲染树后完成布局和绘制,若有 JS 则穿插执行并动态修改页面

连接断开 :通信完成后,通过 TCP 四次挥手断开连接(适配全双工特性,确保数据传输完整)

如果面试的时候需要详细说的话,比如三次握手、四次挥手等,那就看上面的详细解析😊

结语

其实我们总在互联网的协议里寻找答案,以为丢包是意外,延迟是考验,超时是暂时。可直到走过完整的 URL 流程才明白,有些连接从 DNS 解析就注定无果,有些请求再怎么三次握手、四次挥手,也换不来一次响应。就像输入网址后,服务器可以拒绝连接,可以返回错误,可以断开链路,却唯独不会沉默到底。而人生里最真实的道理,从来都藏在这些冰冷的协议中:不必再为一个不愿与你建立连接的人耗尽握手,也别在一段没有响应的关系里反复重传。放过自己,不是停止发送,而是主动挥手,断开这段本就不存在的连接,去遇见那个愿意与你完整完成三次握手、稳稳相伴、好好告别的人。