浏览器是如何工作的?
浏览器是如何工作的?
(从输入URL到页面展现之间,浏览器做的事)
一、浏览器的构成
浏览器从原理上分为7个模块,分别为User Interface(用户界面)、Browser engine(浏览器引擎)、Rendering engine(渲染引擎)、Networking(网络)、JavaScript Interpreter(js解释器)、UI Backend(UI后端)、Date Persistence(数据持久化存储)
二、整体过程
DNS解析 ----> TCP连接 ----> HTTP请求 ----> 构建DOM树 ----> 构建CSSOM树 ----> 构建渲染树 ----> 绘制页面
(从 HTTP 请求回来,就产生了流式的数据,后续的 DOM 树构建、CSS 计算、渲染、合成、绘制,都是尽可能地流式处理前一步的产出:即不需要等到上一步骤完全结束,就开始处理上一步的输出,这样我们在浏览网页时,才会看到逐步出现的页面。
三、DNS解析
首先,DNS(Domain Name System)协议位于应用层,为其他应用层协议工作。
-
因为网络通讯大部分是基于TCP/IP的,而TCP/IP是基于IP地址的,所以计算机在网络上进行通讯时只能识别如“202.96.134.133”之类的IP地址,而不能认识域名。所以需要通过DNS服务器将URL转换成IP地址。
-
URL ---(DNS解析)---> IP地址
-
URL(统一资源定位符)格式 : 协议类型://服务器地址[:端口号]/路径/文件名[参数=值]
DNS解析过程:
-
在浏览器中输入域名www.baidu.com,会先进行系统缓存查询:首先浏览器会调用一个库函数,检测本地的 hosts 文件(可以认为是电脑本地的一个地址映射文件),从该文件中查看是否有对应的该域名的 IP 地址,这个过程是在系统缓存中查找是否存在该域名对应的 IP 地址。如果有,就返回给浏览器,完成域名解析,如果没有,我们继续往下进行。
-
路由器缓存、ISP缓存:如果系统缓存没有,就会向 DNS 服务器发送请求,而网络服务一般都会先经过路由器以及网络服务商(电信),所以会先查询路由器缓存,然后再查询 ISP 的 DNS 缓存。(PS:ISP缓存,本身是一种宽带接入提供商给网页批量访问加速的技术。ISP会将当前访问量较大的网页内容放到ISP服务器的缓存中,当有新的用户请求相同内容时,可以直接从缓存中发送相关信息,不必每次都去访问真正的网站,从而加快了不同用户对相同内容的访问速度,同时也能节省网间流量结算成本。)
-
DNS递归查询:如果路由器缓存和 ISP 的 DNS 缓存还是没有的话,我们就进行 DNS 递归查询。从根域名服务器开始查询,然后再到顶级域名服务器,最后到主域名服务器依次查询。//迭代查询:DNS 收到请求时,而不是直接返回查询结果,而是告诉客户端另一台 DNS 服务器地址。然后客户端再向这台的 DNS 服务器提交请求,依次循环。
-
如何进行DNS优化:① DNS 查询经历很多步骤,查询很慢。浏览器获取到 IP 地址后,一般都会加到浏览器的缓存中,本地的 DNS 缓存服务器,也可以去记录。 ② 使用 DNS 负载均衡,通常我们的网站应用各种云服务,DNS 系统根据每台机器的负载量,地理位置的限制等等,去提供高效快速的 DNS 解析服务。
四、TCP连接
首先,TCP(Transmission Control Protocol,传输控制协议)属于传输层协议,是一种面向连接、可靠的、点到点通信的、面向字节流的传输层通信协议。
TCP连接建立的过程称为"三次握手",连接释放过程为"四次握手/挥手"。
TCP报文段格式:
-
源端口,目标端口:分别代表从哪个规定的串口发送到对方接收的窗口。不同的应用程序都有着不同的端口。
> 应用程序的端口号和应用程序所在主机的 IP 地址统称为 socket(套接字),IP:端口号, 在互联网上 socket 唯一标识每一个应用程序,源端口+源IP+目的端口+目的IP称为”套接字对“,一对套接字就是一个连接,一个客户端与服务器之间的连接。
-
序列号(Seq):用于 TCP 通信过程中某一传输方向上字节流的每个字节的编号,为了确保数据通信的有序性,避免网络中乱序的问题。
-
确认号(ack):确认序列号是接收确认端所期望收到的下一序列号(ack=Seq+1)。确认序号应当是上次已成功收到数据字节序号加1,只有当标志位中的 ACK 标志为 1 时该确认序列号的字段才有效。主要用来解决不丢包的问题。若确认号=N,则表明:到序号N-1为止的所有数据都已正确收到。
-
标志位(TCP FLag):有6个标志比特,主要用于操控TCP状态机,分别为URG,ACK,PSH,RST,SYN,FIN。
-
ACK(Acknowledge character确认字符,表示确认号字段有效,不消耗序号)发送端发送数据到接收端,发送的时候 ACK 为 0,标识接收端还未应答,一旦接收端接收数据之后,就将 ACK 置为 1,发送端接收到之后,就知道了接收端已经接收了数据。
-
SYN(Synchronize Sequence Numbers同步序列编号,消耗一个序号)用来建立 TCP 的连接。SYN 标志位和 ACK 标志位搭配使用,当连接请求的时候,SYN=1,ACK=0连接被响应的时候,SYN=1,ACK=1;这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有 SYN 的数据包,如果对方主机响应了一个数据包回来 ,就表明这台主机存在这个端口。
-
FIN(Final?用来释放一个连接, 消耗一个序号)表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送FIN标志位的 TCP 数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描。
-
窗口大小:表示当前允许发送方发送的数据量,用来进行流量控制。
【三次握手】
为什么进行TCP三次握手:第一,为了确认双方的接收与发送能力是否正常。第二,指定自己的初始化序列号,为后面的可靠传送做准备。第三,如果是 https 协议的话,三次握手这个过程,还会进行数字证书的验证以及加密密钥的生成到。
TCP三次握手:
(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SEND状态,等待Server确认。
(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。
【四次挥手】
由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。
(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。(半关闭状态)
(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图:
> 为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
答: 这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
> 为什么不是一次、二次握手?
答:为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。如果此时客户端发送的延迟的握手信息服务器收到,然后服务器进行响应,认为客户端要和它建立连接,此时客户端并没有这个意思,但 server 却以为新的运输连接已经建立,并一直等待 client 发来数据。这样,server 的很多资源就白白浪费掉了。
五、HTTP连接
HTTP协议位于应用层,使用了TCP提供的服务,HTTP协议的默认端口号为80。
-
客户端与服务端通过 TCP 的三次握手建立连接之后,客户端开始向服务器主动发起请求。这个请求一般是GET或POST请求。
-
服务端接收到客户端发送的信息后进行处理,返回响应信息和文件。返回200状态码则表示远程服务器正确响应。
客户端通过状态码来判断服务端是否成功返回:
-
1XX(信息性状态码) : 服务器正在处理请求中。
-
2XX (成功状态码): 请求处理完毕。
-
3XX (重定向状态码): 需要附加操作以完成请求。
-
301:永久性重定向。该状态码表示请求的资源已被分配了新的 URI,以后使用该资源,使用现在所指 URI。
-
302:临时性重定向。表示该状态码被分配了新的 URI,希望用户本次能够使用新的 URI 访问。
-
304:服务器资源未改变,可直接使用客户端未过期的缓存。
-
4XX (客户端错误状态码): 服务器无法处理请求。
-
400:该请求报文中有语法错误。
-
403:没有资源的访问权限。
-
5XX (服务端错误状态码): 服务器处理请求出错。
-
500:服务器发生错误
-
503:服务器超荷载或正在维护。
六、构建DOM树
服务器将 HTML、CSS、JS文件转化为 0,1字节数据在网络中传输给浏览器,浏览器通过判断状态码开始接收、解析文件,这开始运用到浏览器的渲染原理。
-
首先浏览器要做的就是获取 HTTP 的 Request 的 body 中字符串(字符流)的 HTML 文本,进行解析并构建 DOM 树。
-
将字符流转化为字符串之后,浏览器开始进行词法分析,虽然这个名词我们不熟悉,但是我们要知道,一个 HTML 字符串我们要拆分开才能构建 DOM 树,词法分析就是将字符串拆分成的过程。将字符串转化为的 token(标记) —— token 作为代码的最小单位,也就是拆分后的结果,这个过程我们称为标记化。
-
我们将字符串拆解之后,然后将这些标记转化为 Node 结点,浏览器根据不同的结点开始构建一棵 DOM 树。这就是整个 DOM 树构建的过程。
七、构建CSSOM树
浏览器已经把 HTML 文件转化为了 DOM 树,下面就对 CSS 样式文件进行解析,构建成 CSSOM 树。这个过程和上述构建 DOM 树的过程有点相似,但是其中 CSSOM 树的构建更加的耗时。
-
浏览器通过递归的方式 DOM 树为结点设置样式。通过先找到具体的标签,然后递归找到设置的上级标签,最后确定选择器选择的所选标签的样式。
-
首先在 HTML 中找到 p 标签,一共有两个地方,然后按照样式的继续递归中找具有父节点的 p,我们只好把第二个 p 过滤掉,然后继续向上找父节点 div,匹配成功,然后将样式设置到结点上。
-
我们在写代码的时候可以做出优化,所以应该避免书写过于具体的 CSS 选择器,少一些添加无意义的 HTML 标签,有利于提高习页面的性能。
八、构建渲染树
我们分别将上边生成的 DOM 和 CSSOM 树进行合并,生成我们的渲染树。但是在合并的时候,并不是两者简单的进行结合,因为有些结点我们并不需要显示,还记得有一个 display:none 属性吗?如果某结点的样式有这个属性,就不会出现在渲染树中。
九、绘制页面
【合成、绘制】浏览器在生成渲染树的时候,就会根据渲染树进行布局,调用 GPU 进行绘制,然后合成图层,最后显示在屏幕上。
本文参考: