细说浏览器输入URL后发生了什么

整体概览

大致上,能够分为六步,固然每一步均可以详细展开来讲,这里先放一张总览图:
GitHubhtml

DNS域名解析

在网络世界,你确定记得住网站的名称,可是很难记住网站的 IP 地址,于是也须要一个地址簿,就是 DNS 服务器。DNS 服务器是高可用、高并发和分布式的,它是树状结构,如图:
GitHub前端

  • 根 DNS 服务器 :返回顶级域 DNS 服务器的 IP 地址
  • 顶级域 DNS 服务器:返回权威 DNS 服务器的 IP 地址
  • 权威 DNS 服务器 :返回相应主机的 IP 地址

DNS的域名查找,在客户端和浏览器,本地DNS之间的查询方式是递归查询;在本地DNS服务器与根域及其子域之间的查询方式是迭代查询;面试

递归过程:
GitHubsegmentfault

在客户端输入 URL 后,会有一个递归查找的过程,从浏览器缓存中查找->本地的hosts文件查找->找本地DNS解析器缓存查找->本地DNS服务器查找,这个过程当中任何一步找到了都会结束查找流程。浏览器

若是本地DNS服务器没法查询到,则根据本地DNS服务器设置的转发器进行查询。若未用转发模式,则迭代查找过程以下图:
GitHub缓存

结合起来的过程,能够用一个图表示:
GitHub
在查找过程当中,有如下优化点:安全

  • DNS存在着多级缓存,从离浏览器的距离排序的话,有如下几种: 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。
  • 在域名和 IP 的映射过程当中,给了应用基于域名作负载均衡的机会,能够是简单的负载均衡,也能够根据地址和运营商作全局的负载均衡。

创建TCP链接

首先,判断是否是https的,若是是,则HTTPS实际上是HTTP + SSL / TLS 两部分组成,也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会经过TLS进行加密,因此传输的数据都是加密后的数据。服务器

进行三次握手,创建TCP链接。微信

  1. 第一次握手:创建链接。客户端发送链接请求报文段,将SYN位置为1,Sequence Number为x;而后,客户端进入SYN_SEND状态,等待服务器的确认;网络

  2. 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,须要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,本身还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述全部信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态;

  3. 第三次握手:客户端收到服务器的SYN+ACK报文段。而后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕之后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。

SSL握手过程

  1. 第一阶段 创建安全能力 包括协议版本 会话Id 密码构件 压缩方法和初始随机数
  2. 第二阶段 服务器发送证书 密钥交换数据和证书请求,最后发送请求-相应阶段的结束信号
  3. 第三阶段 若是有证书请求客户端发送此证书 以后客户端发送密钥交换数据 也能够发送证书验证消息
  4. 第四阶段 变动密码构件和结束握手协议

完成了以后,客户端和服务器端就能够开始传送数据。更多 HTTPS 的资料能够看这里:

备注

ACK:此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0。TCP协议规定,只有ACK=1时有效,也规定链接创建后全部发送的报文的ACK必须为1。

SYN(SYNchronization):在链接创建时用来同步序号。当SYN=1而ACK=0时,代表这是一个链接请求报文。对方若赞成创建链接,则应在响应报文中使SYN=1和ACK=1. 所以, SYN置1就表示这是一个链接请求或链接接受报文。

FIN(finis)即完,终结的意思, 用来释放一个链接。当 FIN = 1 时,代表此报文段的发送方的数据已经发送完毕,并要求释放链接。

发送HTTP请求,服务器处理请求,返回响应结果

TCP链接创建后,浏览器就能够利用HTTP/HTTPS协议向服务器发送请求了。服务器接受到请求,就解析请求头,若是头部有缓存相关信息如if-none-match与if-modified-since,则验证缓存是否有效,如有效则返回状态码为304,若无效则从新返回资源,状态码为200.

这里有发生的一个过程是HTTP缓存,是一个常考的考点,大体过程如图:
GitHub
其过程,比较多内容,能够参考个人这篇文章《浏览器相关原理(面试题)详细总结一》,这里我就不详细说了~

关闭TCP链接

  1. 第一次分手:主机1(可使客户端,也能够是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;
  2. 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我"赞成"你的关闭请求;

  3. 第三次分手:主机2向主机1发送FIN报文段,请求关闭链接,同时主机2进入LAST_ACK状态;

  4. 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,而后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段之后,就关闭链接;此时,主机1等待2MSL后依然没有收到回复,则证实Server端已正常关闭,那好,主机1也能够关闭链接了。

浏览器渲染

按照渲染的时间顺序,流水线可分为以下几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、栅格化和显示。如图:
GitHub

  1. 渲染进程将 HTML 内容转换为可以读懂DOM 树结构。
  2. 渲染引擎将 CSS 样式表转化为浏览器能够理解的styleSheets,计算出 DOM 节点的样式。
  3. 建立布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成分层树。
  5. 为每一个图层生成绘制列表,并将其提交到合成线程。合成线程将图层分图块,并栅格化将图块转换成位图。
  6. 合成线程发送绘制图块命令给浏览器进程。浏览器进程根据指令生成页面,并显示到显示器上。

构建 DOM 树

浏览器从网络或硬盘中得到HTML字节数据后会通过一个流程将字节解析为DOM树,先将HTML的原始字节数据转换为文件指定编码的字符,而后浏览器会根据HTML规范来将字符串转换成各类令牌标签,如html、body等。最终解析成一个树状的对象模型,就是dom树。
GitHub

具体步骤:

  1. 转码(Bytes -> Characters)—— 读取接收到的 HTML 二进制数据,按指定编码格式将字节转换为 HTML 字符串
  2. Tokens 化(Characters -> Tokens)—— 解析 HTML,将 HTML 字符串转换为结构清晰的 Tokens,每一个 Token 都有特殊的含义同时有本身的一套规则
  3. 构建 Nodes(Tokens -> Nodes)—— 每一个 Node 都添加特定的属性(或属性访问器),经过指针可以肯定 Node 的父、子、兄弟关系和所属 treeScope(例如:iframe 的 treeScope 与外层页面的 treeScope 不一样)
  4. 构建 DOM 树(Nodes -> DOM Tree)—— 最重要的工做是创建起每一个结点的父子兄弟关系

样式计算

渲染引擎将 CSS 样式表转化为浏览器能够理解的 styleSheets,计算出 DOM 节点的样式。

CSS 样式来源主要有 3 种,分别是经过 link 引用的外部 CSS 文件、style标签内的 CSS、元素的 style 属性内嵌的 CSS。,其样式计算过程主要为:
GitHub
能够看到上面的 CSS 文本中有不少属性值,如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,因此须要将全部值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。处理完成后再处理样式的继承和层叠,有些文章将这个过程称为CSSOM的构建过程。

页面布局

布局过程,即排除 script、meta 等功能化、非视觉节点,排除 display: none 的节点,计算元素的位置信息,肯定元素的位置,构建一棵只包含可见元素布局树。如图:
GitHub
其中,这个过程须要注意的是回流和重绘,关于回流和重绘,详细的能够看我另外一篇文章《浏览器相关原理(面试题)详细总结二》,这里就不说了~

生成分层树

页面中有不少复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 作 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还须要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree),如图:
GitHub
若是你熟悉 PS,相信你会很容易理解图层的概念,正是这些图层叠加在一块儿构成了最终的页面图像。在浏览器中,你能够打开 Chrome 的"开发者工具",选择"Layers"标签。渲染引擎给页面分了不少图层,这些图层按照必定顺序叠加在一块儿,就造成了最终的页面。

并非布局树的每一个节点都包含一个图层,若是一个节点没有对应的层,那么这个节点就从属于父节点的图层。那么须要知足什么条件,渲染引擎才会为特定的节点建立新的层呢?详细的能够看我另外一篇文章《浏览器相关原理(面试题)详细总结二》,这里就不说了~

栅格化

合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操做是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。如图:

GitHub

一般一个页面可能很大,可是用户只能看到其中的一部分,咱们把用户能够看到的这个部分叫作视口(viewport)。在有些状况下,有的图层能够很大,好比有的页面你使用滚动条要滚动很久才能滚动到底部,可是经过视口,用户只能看到页面的很小一部分,因此在这种状况下,要绘制出全部图层内容的话,就会产生太大的开销,并且也没有必要。

显示

最后,合成线程发送绘制图块命令给浏览器进程。浏览器进程根据指令生成页面,并显示到显示器上,渲染过程完成。

参考资料

  • 极客时间《趣谈网络协议》
  • 极客时间《浏览器工做原理与实践》

最后

  • 欢迎加我微信(winty230),拉你进技术群,长期交流学习...
  • 欢迎关注「前端Q」,认真学前端,作个有专业的技术人...

GitHub