Skip to content
本页目录

从输入 URL 到页面呈现之间发生了什么?

  1. DNS 解析(将域名解析为 IP 地址)
  2. 发起 TCP 连接(三次握手)
  3. 发送 HTTP 请求
  4. 服务器处理请求并返回 HTTP 报文
  5. 浏览器解析渲染页面
  6. 断开连接(四次挥手)

1. DNS 解析

浏览器输入域名后,会通过DNS服务器查找该域名对应的 IP 地址,然后通过 IP 地址来访问服务器资源

在此插入一个 CDN 的知识,它是利用 DNS 重定向方式,一个 CDN 域名会存在多个服务器, DNS 服务器会返回一个与当前用户距离最近的服务器 IP 地址,以使访问能够更快。

2. TCP 三次握手

  • 第一次:由浏览器发起,告诉服务器要发起请求了

  • 第二次:由服务器发起,告诉浏览器我准备好了

  • 第三次:由浏览器发起,我收到了,准备发起请求了

  • 第一次:客户端发送syn包(Seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;

  • 第二次:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(Seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;

  • 第三次:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

3. 发送 HTTP 请求

TCP 三次握手结束后,开始发送 HTTP 请求

4. 服务器处理请求并返回 HTTP 报文

5. 浏览器解析渲染页面

  1. 解析 HTML ,构建 DOM
  2. 解析 CSS ,生成 CSS 规则树
  3. 合并 DOMCSS 规则,生成 render
  4. 布局 render 树(Layout/Reflow 回流),负责各元素尺寸、位置的计算
  5. 绘制 render 树(paint 重绘),绘制页面像素信息

注意:

  • 构建 DOM 树是一个渐进过程,为达到更好的用户体验,渲染引擎会尽快将内容显示在屏幕上,它不必等到整个 HTML 文档解析完成之后才开始构建 render 树和布局。
  • Render 树不必等到 DOM 树和 CSS 样式表构建完毕后才开始构建,这三个过程在实际进行的时候并不是完全独立的,而是会有交叉,会一边加载,一边解析,以及一边渲染。
  • CSS 的解析是从右往左逆向解析的,嵌套标签越多,解析越慢。
  • 原生 JSJQ 操作 DOM 时,浏览器会从构建 DOM 树开始从头到尾执行一遍流程。在一次操作中,我需要更新 10 个 DOM 节点,浏览器收到第一个 DOM 请求后并不知道还有 9 次更新操作,因此会马上执行流程,最终执行10 次。例如,第一次计算完,紧接着下一个 DOM 更新请求,这个节点的坐标值就变了,前一次计算为无用功。计算 DOM 节点坐标值等都是白白浪费的性能。

6. 断开连接

数据传输完毕,需要断开 TCP 连接,此时将发起 TCP 四次挥手

  • 第一次:由浏览器发起,发送给服务器,请求报文已发送完,请准备关闭连接

  • 第二次:由服务器发起,发送给浏览器,请求报文已接收完,我准备关闭连接

  • 第三次:由服务器发起,发送给浏览器,响应报文已发送完,你准备关闭连接

  • 第四次:由浏览器发起,发送给服务器,响应报文已接收完,我准备关闭连接,你也关闭吧

  • 第一次挥手:
    客户端发送一个FIN,用来关闭客户端到服务器的数据传送,也就是客户端告诉服务器:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,客户端依然会重发这些数据),但是,此时客户端还可 以接受数据。 FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。

  • 第二次挥手:
    服务器收到FIN包后,发送一个ACK给对方并且带上自己的序列号seq,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。 此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。

  • 第三次挥手:
    服务器发送一个FIN,用来关闭服务器到客户端的数据传送,也就是告诉客户端,我的数据也发送完了,不会再给你发数据了。由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

  • 第四次挥手:
    主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。 至此,完成四次挥手。

为什么是三次握手?

三次握手中,clientserver 都有一个发 syn 和收 ack 的过程, 双方都是发后能收, 表明通信则准备工作OK。

这是由 TCP 的自身特点可靠传输决定的。客户端和服务端要进行可靠传输,那么就需要确认双方的接收和发送能力。第一次握手可以确认客服端的发送能力,第二次握手,确认了服务端的发送能力和接收能力,所以第三次握手才可以确认客户端的接收能力。不然容易出现丢包的现象。