浏览器打开www.baidu.com的流程

1,847 阅读12分钟

概述

  1. www.baidu.com 这个网址进行DNS域名解析,得到对应的IP地址
  2. 根据这个IP,找到对应的服务器,发起TCP的三次握手
  3. 建立TCP连接后发起HTTP请求
  4. 服务器响应HTTP请求,浏览器得到HTML代码
  5. 浏览器解析HTML代码,并请求HTML代码中的资源(js、css、图片等)
  6. 浏览器对页面进行渲染呈现给用户
  7. 服务器关闭TCP连接

注: 为什么HTTP协议要基于TCP来实现?

TCP是一个端到端的可靠的面向连接的协议,HTTP基于传输层TCP协议不用担心数据传输的各种问题(当发生错误时,会重传)

浏览器的渲染过程:

解析文件

  • html文件转换为DOM树
  • css文件转换为CSSOM树
  • 将DOM树和CSSOM树合并生成渲染树

绘制图层

  • 根据渲染树生成布局渲染树(回流)
  • 根据布局渲染树生成绘制渲染树(重绘)

合成图层

  • 根据绘制渲染树合成图层显示在屏幕上

各个步骤具体细节

DNS解析(域名解析服务器)

访问过程:

  • 浏览器会首先看自身有没有对这个域名的缓存,如果有就直接返回,如果没有就去问操作系统
  • 操作系统里会对 DNS 解析结果做缓存,如果缓存中有直接返回 IP 地址
  • 如果还没有找到,那么尝试从hosts文件里面去找
  • 在前面三个过程都没有获取到的情况下,就递归地去域名服务器去查找,具体过程如下
    • 客户端发出DNS请求,并发送给本地 DNS 服务器。
    • 本地域名服务器的缓存中没有,就去问根 DNS 服务器(.)
    • 根 DNS 服务器收到本地 DNS 的请求后,发现后置是 .com,就给.com 顶级域名服务器地址,接着问顶级域名服务器
    • 顶级域名服务器给出负责 www.server.com 区域的权威 DNS 服务器的地址,接着去问权威 DNS 服务器
    • 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。
    • 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。

6.webp

DNS在哪些场景下会用TCP / UDP? DNS缓存中,原始的域名和IP地址发生改变,那需要过一段时间去更新缓存中的数据,更新的时候又向上发一次请求,请求IP地址,对比缓存中的IP。这次更新的请求就是 TCP 。但是查询的时候使用的是UDP。 原因:数据包数量 2/9,效率和代价的影响,还有DNS数据载荷非常小,是小量的数据传输,使用UDP的时候能够承载的了,但是更新的时候,追求反而是数据传输的可靠性,所以采用TCP。

TCP连接建立(三次握手)

TCP三次握手.drawio.webp

  • 一开始,客户端和服务端都处于CLOSED状态。先是服务端主动监听某个端口,处于LISTEN状态
  • 然后客户端主动发起连接SYN,之后处于SYN-SENT状态
  • 服务端收到发起的连接,返回SYN,之后处于SYN-SENT状态
  • 服务端收到发起的连接,返回SYN,之后处于SYN-RCVD状态
  • 客户端收到服务端发送的SYNACK之后,发送对SYN确认的ACK,之后处于ESTABLISHED状态,因为它一发一收成功了。
  • 服务端收到ACKACK之后,处于 ESTABLISHED 状态,因为它也一发一收了。

所以三次握手目的是保证双方都有发送和接收的能力

发起HTTP请求(建立连接后)

HTTP请求报文由三部分组成:请求行、请求头、空格、请求正文

请求行:用于描述客户端的请求方式(GET/POST等),请求的资源名称(URL)以及使用的HTTP协议的版本号

请求头:用于描述客户端请求哪台主机及其端口,以及客户端的一些环境信息等

空行:空行就是\r\n(POST请求的时候有)

请求正文:当使用POST等方法的时候,通常需要客户端向服务器传递数据。这些数据就存储在请求正文中(GET方法是保存在url地址后面,不会放到这里)

GET请求 image.png POST请求 image.png

服务器响应http请求,浏览器得到html代码

HTTP响应也由三部分组成:状态行、响应头、空格、消息体 状态行包括:协议版本、状态码、状态码描述 状态码:状态码用于标识服务器对请求的处理结果

1xx: 指示信息——标识请求已经接受,继续处理

2xx: 成功——表示请求已经被成功接收、理解、接受

3xx: 重定向——要完成请求必须更进一步的操作

4xx: 客户端错误——请求有语法错误或请求无法实现

5xx: 服务器端错误——服务器未能实现合法的请求

常见:200(OK)、204(响应头没有 body 数据)、206(返回的 body 数据并不是资源的全部,而是其中的一部分)、301(永久重定向,需要改用新的URL)、302(临时重定向)、304(重定向已存在的缓冲文件)、403(服务器禁止访问资源,并不是客户端的请求出错)、404(请求的资源在服务器上不存在或未找到)、500(服务器这边有问题)

响应头:用于描述服务器的基本信息,以及客户端如何处理数据

空格:CRLF(\r\n)分割

消息体:服务器返回给客户端的数据

image.png

解析文件

HTML文档描述一个页面的结构,浏览器通过HTML解析器将HTML解析成DOM树结构。HTML文档中所有内容皆为节点,各节点间拥有层级关系,彼此相连,构成DOM树。构建DOM树的过程:读取HTML文档的字节,将字节转化成字符,依据字符确定标签,将标签转换成节点,以节点为基准构建DOM树

浏览器通过CSS解析器将CSS解析成CSSOM树结构,与DOM树结构较像。CSS文档中所有内容皆为节点,与HTML文档中的节点一一对应,各节点间拥有层级关系,彼此相连,构成CSSOM树。构建CSSOM树的过程:读取CSS文档的字节(Bytes),将字节转换成字符(Chars),依据字符确定标签(Tokens),将标签转换成节点(Nodes),以节点为基准构建CSSOM树。与DOM树的构建过程完全一致。

在构建DOM树的过程中,当HTML解析器遇到<script>时会立即阻塞DOM树的构建,将控制权移交给浏览器的JS引擎,等到JS引擎运行完毕,浏览器才会从中断的地方恢复DOM树的构建。<script>的脚本加载完毕,JS引擎通过DOM APICSSOM API操作DOM树和CSSOM树。为何会产生渲染阻塞呢?其根本原因在于:JS操作DOM后,浏览器无法预测未来DOM的具体内容,为了防止无效操作和节省资源,只能阻塞DOM树的构建。

Snipaste_2023-09-03_11-30-54.png

绘制图层

进入绘制阶段,遍历渲染树,调用渲染器的paint()在屏幕上绘制内容。根据渲染树布局计算布局样式,即每个节点在页面中的布局、尺寸等几何属性。HTML默认是流式布局,CSS和JS会打破这种布局,改变DOM的集合属性和外观属性。在绘制过程中,根据渲染树布局,再根据布局绘制,这就是回流重绘

  • 回流:几何属性需改变的渲染
  • 重绘:更改外观属性而不影响几何属性的渲染

当生成渲染树后,至少会渲染一次。在后续交互过程中,还会不断地重新渲染。这时只会回流重绘或只有重绘。因此引出一个定向法则:回流必定引发重绘,重绘不一定引发回流。

服务器关闭TCP连接

format,png-20230309230614791.webp

  • 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。
  • 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSE_WAIT 状态。
  • 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
  • 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
  • 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
  • 服务端收到了 ACK 应答报文后,就进入了 CLOSE 状态,至此服务端已经完成连接的关闭。
  • 客户端在经过 2MSL 一段时间后,自动进入 CLOSE 状态,至此客户端也完成连接的关闭。

为什么挥手需要四次?

再来回顾下四次挥手双方发 FIN 包的过程,就能理解为什么需要四次了。

  • 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务端收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。

从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,因此是需要四次挥手。

性能优化

1. 利用缓存

  • 对于静态资源文件实现强缓存和协商缓存
    • 文件有更新,如何保证及时更新?(文件指纹: 使用构建工具生成文件的唯一指纹(哈希值)作为文件名的一部分。这确保了每次文件内容发生更改时,文件名都会变化,从而触发浏览器重新下载;每次文件更新时,修改文件名,这会迫使浏览器重新下载文件,因为文件名不同视为不同的资源。)
  • 对于不经常更新接口数据采用本地缓存做数据缓存

2.DNS优化

  • 分服务器部署,增加HTTP并发性(导致DNS解析变慢)
  • DNS Prefetch(DNS 预解析)
    • 当浏览器加载网页时,它会分析网页中的链接(如 <a> 标签、CSS 文件、JavaScript 文件等)。
    • 如果浏览器检测到一个链接,它会尝试预测用户可能会点击的链接,并预先解析这些链接的主机名(即域名),将域名解析成 IP 地址。
    • 浏览器会在后台执行这些 DNS 查询,而不会阻塞页面的加载。这意味着当用户点击链接时,浏览器已经知道了目标服务器的 IP 地址,可以更快地建立连接。
    • <link rel="dns-prefetch" href="https://example.com">

3.TCP的三次握手四次挥手

  • Connection:keep-alive,使用持久连接,客户端可以发出多个并行请求,而不必等待之前的请求完成。这有助于提高页面加载速度。

4.数据传输

  • 减少数据传输的大小
    • 内容或者数据压缩(webpack)
    • 服务端开启GZIP压缩(一般能压缩60%)
    • 大批量数据分批次请求(例如:下拉刷新或者分页,保证首次加载请求次数少)
  • 减少HTTP请求的次数
    • 资源文件合并处理
    • 字体图标:展示的是图标,本质属于字体
    • 雪碧图
    • 图片的BASE64:将图片文件编码成 BASE64 字符串后,可以将其嵌入到网页的 HTML 或 CSS 中,而不必通过 HTTP 请求来获取图片。

5.CDN服务器“地域式分布”

地域式分布的 CDN 架构有助于降低网络拥塞、提高网站性能、加速内容传输,并提供更好的用户体验。通过将内容存储在全球各地,CDN 使用户能够更快地访问网站,而不受地理位置的限制。这对于在线内容提供商、电子商务网站和各种在线服务来说都非常重要。

6.采用HTTP2.0

  • 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本,基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合,基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮
  • header压缩,HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小
  • 服务端推送(server push),例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了
  • 多路复用(MultiPlexing)
    • HTTP/1.0 每次请求响应,建立一个TCP连接,用完关闭
    • HTTP/1.1 「长连接」 若干个请求排队串行化单线程处理,后面的请求等待前面请求的返回才能获得执行机会,一旦有某请求超时等,后续请求只能被阻塞,毫无办法,也就是人们常说的线头阻塞;
    • HTTP/2.0 「多路复用」多个请求可同时在一个连接上并行执行,某个请求任务耗时严重,不会影响到其它连接的正常执行;

减少白屏的效果和时间

  • LOADING: 人性化体验
  • 骨架屏:客户端骨屏 + 服务器骨架屏
  • 图片延迟加载