从输入url到展示页面过程发生了什么?

476 阅读8分钟

整个过程可以分为几步:

  • 用户输入
  • URL 请求过程
    • DNS解析
    • TCP连接
    • 发送HTTP请求
    • 服务器处理请求并返回HTTP报文
  • 浏览器解析渲染页面

1. 用户输入

当用户按下回车之后,这意味着当前页面即将要被替换成新的页面,
不过在这个流程继续之前,浏览器还给了当前页面一次执行 beforeunload 事件的机会,
beforeunload 事件允许页面在退出之前执行一些操作,比如数据清理,还可以询问用户是否要离开当前页面。

2. URL 请求过程

首先,会查找本地缓存是否缓存了想要请求资源。
如果有缓存资源,那么直接返回资源给浏览器进程;
如果在缓存中没有查找到资源,那么直接进入网络请求流程。
这请求前的第一步是要进行DNS解析,以获取请求域名的服务器IP地址。
如果请求协议是 HTTPS,那么还需要建立 TLS 连接。

  • DNS解析:

    其中,DNS解析是一个递归查询的过程:,

    1. DNS缓存

      首先在浏览器缓存,hosts文件中查询IP地址,如果没有找到,会向根域名服务器发送一个请求,如果根域名服务器也不存在,本地域名会向com顶级域名服务器发送一个请求,依次类推下去。直到最后本地域名服务器得到请求域名的IP地址并把它缓存到本地,供下次查询使用

    2. DNS负载均衡

      DNS返回的IP地址是否每次都一样?
      如果每次都一样是否说明你请求的资源都位于同一台机器上面,
      那么这台机器需要多高的性能和储存才能满足亿万请求呢?
      其实真实的互联网世界背后存在成千上百台服务器,大型的网站甚至更多。
      但是在用户的眼中,它需要的只是处理他的请求,哪台机器处理请求并不重要。
      DNS可以返回一个合适的机器的IP给用户
      例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,这种过程就是DNS负载均衡,又叫做DNS重定向。
      CDN(Content Delivery Network)就是利用DNS的重定向技术,
      DNS服务器会返回一个跟用户最接近的点的IP地址给用户
      ,CDN节点的服务器负责响应用户的请求,提供所需的内容。

  • TCP连接:

    TCP三次握手(Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。
    进行三次握手的主要作用:就是为了确认双方的接收能力发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。

    • 第一次握手:客户端发送网络包,服务端收到了。 这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
    • 第二次握手:服务端发包,客户端收到了。 这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
    • 第三次握手:客户端发包,服务端收到了。 这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

    实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。

  • 挥手为什么需要四次?

    因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。

    四次挥手的过程如下:

    1. 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。 即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。

    2. 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。 即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。

    3. 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。 即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。

    4. 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。 即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。

  • SYN攻击是什么?

    服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

接下来就是利用 IP地址和服务器建立TCP连接。连接建立之后,浏览器端会构建请求行、请求头等信息,并把和该域名相关的Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。

数据在进入服务端之前,可能还会先经过负责负载均衡的服务器,它的作用就是将请求合理的分发到多台服务器上,这时假设服务端会响应一个 HTML 文件。

首先浏览器会判断状态码是什么,如果是 200 那就继续解析,如果 400500 的话就会报错,如果 300 的话会进行重定向,这里会有个重定向计数器,避免过多次的重定向,超过次数也会报错。

浏览器开始解析文件,如果是 gzip 格式的话会先解压一下,然后通过文件的编码格式知道该如何去解码文件。

3. 准备渲染进程

默认情况下,浏览器会为每个页面分配一个渲染进程,也就是说,每打开一个新页面就会配套创建一个新的渲染进程。

4. 渲染阶段

文件解码成功后会正式开始渲染流程,先会根据HTML构建DOM树,有CSS的话会去构建 CSSOM 树。如果遇到 script 标签的话,会判断是否存在 async 或者 defer ,前者会并行进行下载并执行JS,后者会先下载文件,然后等待gzipHTML解析完成后顺序执行。

如果以上都没有,就会阻塞住渲染流程直到 gzipJS 执行完毕。

gzipCSSOM 树和 gzipDOM 树构建完成后会开始生成 gzipRender 树,这一步就是确定页面元素的布局、样式等等诸多方面的东西

在生成 gzipRender 树的过程中,浏览器就开始调用gzipGPU 绘制,合成图层,将内容显示在屏幕上了。

参考文章如下:

juejin.im/post/684490…

segmentfault.com/a/119000000…

juejin.cn/post/693163…