前言
为了方便接下来的介绍,我们首先要了解计算机网络体系结构,如下图:
从 url 输入到页面展现的总体过程:
用户输入
检查缓存
DNS 解析
建立 TCP 连接(三次握手)
HTTP 请求与响应
关闭 TCP 连接(四次挥手)
准备渲染进程
提交文档
渲染阶段
1 用户输入
当用户在地址栏中输入一个查询关键字时,地址栏会判断是搜索内容,还是URL。
- 如果是搜索内容,地址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URL。
- 如果判断输入内容符合 URL 规则,则地址栏会根据规则,把这段内容加上协议,合成为完整的 URL。
2 检查缓存
网络进程会查找本地缓存是否缓存了该资源。
- 如果有缓存资源,那么直接返回资源给浏览器进程;
- 如果在缓存中没有查找到资源,那么直接进入网络请求流程。
3 DNS 解析
3.1 什么是 DNS
域名系统 DNS是互联网使用的命名系统,用来把域名转换为 IP 地址。
3.2 DNS 解析的查询过程
DNS 解析过程中,主机向本地域名服务器的查询一般都是采用递归查询,本地域名服务器向根域名服务器的查询通常是采用迭代查询。
以上图(a)例,具体的 DNS 解析步骤为:
主机 m.xyz.com 先向其本地域名服务器 dns.xyz.com 进行递归查询。
本地域名服务器采用迭代查询。它先向一个根域名服务器查询。
根域名服务器告诉本地域名服务器,下一次应查询的顶级域名服务器 dns.com 的 IP 地址。
本地域名服务器向顶级域名服务器 dns.com 进行查询。
顶级域名服务器 dns.com 告诉本地域名服务器,下一次应查询的权限域名服务器 dns.abc.com 的 IP 地址。
本地域名服务器向权限域名服务器 dns.abc.com 进行查询。
权限域名服务器 dns.abc.com 告诉本地域名服务器,所查询的主机的 IP 地址。
本地域名服务器最后把查询结果告诉主机 m.xyz.como。
思考:DNS 为什么使用 UDP 协议作为传输层协议? 答:主要原因是为了避免使用 TCP 协议时造成的连接时延。
3.3 DNS 优化
DNS 缓存: 浏览器缓存,系统缓存,路由器缓存,IPS 服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。通过缓存直接读取域名相对应的 IP,减去了繁琐的查找 IP 的步骤,大大加快访问速度。
DNS 负载均衡:使用 CDN 进行负载均衡,利用 DNS 的重定向技术,同步服务器运行情况,然后根据该情况及时适当调整调度策略,从而使得负载均衡能力大大提高。
4 建立 TCP 连接
注:本节大部分内容摘自参考[1]。
4.1 什么是 TCP
TCP 传输控制协议有以下这些特点:
TCP 是面向连接的运输层协议。
每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的。
TCP 提供可靠交付的服务,即传输的数据无差错、不丢失、不重复、按序到达。
TCP 提供全双工通信。
面向字节流。
4.2 TCP 报文段
TCP 报文段的首部格式:
其中部分字段的含义:
序号:指的是本报文段所发送的数据的第一个字节的序号。
确认号:是指期望收到对方下一个报文段的第一个数据字节的序号。若确认号 =N, 则表明:到序号 N-1 为止的所有数据都己正确收到。
确认 ACK:仅当 ACK= 1 时确认号字段才有效。当 ACK=0 时,确认号无效。 TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。
同步 SYN:在连接建立时用来同步序号。当 SYN= 1 而 ACK = 0 时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN=1 和 ACK= 1。因此, SYN 置为 1 就表示这是一个连接请求或连接接受报文。
终止 FIN:用来释放连接。但 FIN 置为 1 时,表示报文发送发的数据已发送完毕,并要求释放运输连接。
4.3 三次握手的具体过程
TCP 连接的过程又称为三次握手,示意图如下:
三次握手的具体过程: 一开始, B 的 TCP 服务器进程先创建传输控制块 TCB,准备接受客户进程的连接请求。然后服务器进程就处于 LISTEN (收听)状态,等待客户的连接请求。如有,即作出响应。
A 的 TCP 客户进程也是首先创建传输控制模块 TCB。然后,在打算建立 TCP 连接时, 向 B 发出连接请求报文段,这时首部中的同步位 SYN = 1 ,同时选择一个初始序号 seq = x。TCP 规定, SYN 报文段(即 SYN= 1 的报文段)不能携带数据,但要消耗掉一个序号。 这时, TCP 客户进程进入 SYN-SENT (同步己发送)状态。
B 收到连接请求报文段后,如同意建立连接,则向 A 发送确认。在确认报文段中应把 SYN 位和 ACK 位都置 1 ,确认号是 ack = x + 1,同时也为自己选择一个初始序号 seq = y。 请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。这时 TCP 服务器进程进 入 SYN-RCVD (同步收到)状态。
TCP 客户进程收到 B 的确认后,还要向 B 给出确认。确认报文段的 ACK 置 1 ,确认号 ack=y+1,而自己的序号 seq = x + 10。TCP 的标准规定, ACK 报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下,下一个数据报文段的序号仍是 seq=x+1。这时, TCP 连接己经建立, A 进入 ESTABLISHED (己建立连接)状态。 当 B 收到 A 的确认后,也进入 ESTABLISHED 状态。
思考:为什么 A 最后还要发送一次确认呢? 这主要是为了防止己失效的连接请求报文段突然又传送到了服务器,因而产生错误。
5 HTTP 请求与响应
5.1 HTTP 简介
HTTP 是基于 TCP/IP 协议的应用层协议。它的主要特点是:
灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记。
无状态:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
请求-应答模式,通常而言,就是一方发送消息,另外一方要接受消息,或者是做出相应等。
可靠传输,HTTP 是基于 TCP/IP,因此把这一特性继承了下来。
5.1.1 请求报文
HTTP 请求报文由请求行
、请求头
、请求体
组成。
5.1.2 响应报文
HTTP 响应报文由响应行
、响应头
、响应体
组成。
在响应报文中,Content-Type 字段的值:
- 如果被浏览器判断为下载类型,那么该请求会被提交给浏览器的下载管理器,同时该 URL 请求的导航流程就此结束。
- 但如果是 HTML,那么浏览器则会继续进行导航流程。
查阅资料:常见状态码
6 关闭 TCP 连接
数据传输完成后,如果没有设置 Connection:Keep-Alive,将关闭 TCP 连接。
关闭 TCP 连接的具体步骤,如下图:
A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。 A 把终止控制位 FIN 置 1 ,序号 seq = u,u 等于前面已传送过的数据的最后一个字节的序号加 1 。
B 收到连接释放报文段后,则发出确认,确认号是 ack = u + 1,序号是 v,等于 B 前面已传送过的数据的最后一个字节的序号加 1 。这时的 TCP 连接处于半关闭状态,即 A 已经没有数据要发送了,但 B 若发送数据, A 仍要接收。
若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接。这时 B 发出的连接释放报文段中 FIN = 1 。现假定 B 的序号为 w (在半关闭状态 B 可能又发送了一些数据)。B 还必须重复上次已发送过的确认号 ack = u + 1 。
A 在收到 B 的连接释放报文段后,必须对此发出确认。在确认报文段中把 ACK 置 1,确认号 ack = w + 1 ,而自己的序号是 seq=u+1。然后进入到 TIME-WAIT (时间等待)状态。现在 TCP 连接还没有释放掉,必须 2MSL(最长报文段寿命) 时间后, A 才进入到 CLOSED 状态,才能开始建立下一个新的连接。
思考:为什么 A 在 TIME-WAIT 状态必须等待 2MSL 的时间呢?
为了保证 A 发送的最后一个 ACK 报文段丢失时,A 可以收到 B超时重传FIN + ACK 报文段的确认。
防止"己失效的连接请求报文段"出现在本连接中。
7 准备渲染引擎
process-per-site-instance:这是一种浏览器的默认策略,具体而言就是:如果从一个页面打开了另一个新页面,而新页面和当前页面属于同一站点的话,那么新页面会复用父页面的渲染进程。
打开新页面采用的渲染进程策略是:
- 如果从 A 页面打开 B 页面,且二者都属于同一站点的话,那么 B 页面复用 A 页面的渲染进程;
- 如果是其他情况,浏览器进程则会为 B 创建一个新的渲染进程。
8 提交文档
提交文档:就是指浏览器进程将网络进程接收到的 HTML 数据提交给渲染进程。
- 浏览器发出“提交文档”的消息给渲染进程
- 渲染进程收到消息后,会和网络进程建立传输数据的“管道”
- 文档数据传输完成
- 渲染进程返回“确认提交”的消息给浏览器进程
- 浏览器收到“确认提交”的消息后,会更新浏览器的页面状态
9 渲染阶段
渲染流水线:渲染模块在执行过程中会被划分为很多子阶段,这些子阶段称为流水线。
渲染引擎的工作流程:以 HTML/JavaScript/CSS 等文件作为输入,以可视化内容作为输出。 具体步骤:
HTML 转换为 DOM树
CSS 转换为 CSSOM
合并DOM和CSSOM,生成渲染树(这棵树中不包含不可见节点)
根据渲染树来布局,以计算每个节点的几何信息。
对布局树进行分层,生成分层树
为每个图层生成绘制列表
合成线程将图层分成图块(tile),并在光栅化(raster)线程池中将图块转换成位图
合成线程发送绘制图块命令 DrawQuad 给浏览器进程
浏览器进程生成页面,并显示到显示器上
渲染流水线示意图,如下:
reflow(重排)、repaint(重绘)和 composite (合并)是什么?
reflow:更新元素的几何属性(如:改变元素的宽度、高度等)时,将发生reflow。重排需要更新完整的渲染流水线,所以开销也是最大的。
repaint:更新元素的绘制属性(如:更改元素的背景颜色)时,将发生repaint。重绘省去了布局和分层阶段,所以执行效率会比重排操作要高一些。
composite:更改一个既不要布局也不要绘制的属性(如:transform)时,将发生composite。相对于重绘和重排,合成能大大提升绘制效率。
如何减少reflow(重排)、repaint(重绘)?
- 将经常改变的元素,设置定位为fix或absolute,以避免影响其周围元素
- 避免不必要的CSS选择器
- 对移除或者隐藏的元素进行修改
- 通过临时存储值,减少测量次数,等等...
参考
《计算机网络第七版》-章节 5.3、5.5、5.9、6.1,谢希仁