从输入URL到页面加载完成的过程中都发生了什么

这是一道经典的面试题,面试官会根据你回答的内容追问下探,既考广度又考深度。关于此问题的文章网上也是一抓一大把,我写这篇博客的目的主要是从前端的角度屡一下这个过程,加深一下理解。

大概流程是这样的:

  1. DNS解析
  2. TCP连接
  3. 发送HTTP请求
  4. 服务器处理请求并返回HTTP报文
  5. 浏览器解析渲染页面
  6. 连接结束

1. 查找 IP 地址

每个域名都对应一个或多个提供相同服务服务器的 IP 地址,只有知道服务器 IP 地址才能建立连接,所以需要通过 DNS 把域名解析成一个 IP 地址。

浏览器会根据浏览器缓存->系统缓存(host)->路由器缓存->DNS服务器,逐级递归向上找,找到该域名对应的IP地址。

DNS 将得到的 IP 地址返回给操作系统,同时自己也将 IP 地址缓存起来;操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来;至此,浏览器已经得到了域名对应的 IP 地址。

补充:

  • 域名与 URL 是两个概念:域名是一台或一组服务器的名称,用来确定服务器在 Internet 上的位置;URL 是统一资源定位符,用来确定某一个文件的具体位置,
  • IP 地址与域名不是一一对应的关系:可以把多个提供相同服务的服务器 IP 设置为同一个域名,但在同一时刻一个域名只能解析出一个 IP地址;同时,一个 IP 地址可以绑定多个域名,数量不限。

2. 建立TCP连接

得到服务器 IP 地址止后,浏览器在传输层发起一条到达服务器的 TCP 连接,建立 TCP 三次握手。

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
SYN:同步序列编号(Synchronize Sequence Numbers)

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

通过这样的三次握手,客户端与服务端建立起可靠的双工的连接,开始传送数据。

为保证信息传输的可靠性,三次握手过程中,若一方收不到确认信号,协议会要求重新发送信号。

3. HTTP发起请求

HTTP 请求报文由四部分组成:请求行、请求头、空行、消息体。

chrome-request-headers

一个典型的 http request header 一般需要包括请求的方法,例如 GET 或者 POST 等,不常用的还有 PUT 和 DELETE 方法,更加不常用的还有 HEAD 和 OPTION 以及 TRACE 方法,一般的浏览器只能发起 GET 或者 POST 请求。

4. 服务器处理请求

服务器在收到浏览器发送的 HTTP 请求之后,会将收到的HTTP报文封装成HTTP的Request对象,并通过不同的Web服务器进行处理,处理完的结果以 HTTP 的 Response 对象返回,主要包括状态码,响应头,响应报文三个部分。

HTTP状态码共分为5种类型:

1** 信息,服务器收到请求,需要请求者继续执行操作

2** 成功,操作被成功接收并处理

3** 重定向,需要进一步的操作以完成请求

4** 客户端错误,请求包含语法错误或无法完成请求

5** 服务器错误,服务器在处理请求的过程中发生了错误

以下最常见的几种 HTTP 状态码:

  • 200 - 请求成功
  • 301 - 资源(网页等)被永久转移到其它URL
  • 404 - 请求的资源(网页等)不存在
  • 500 - 内部服务器错误

5. 页面渲染

当浏览器接收到报文,根据收到的资源的类型,进行语法解析,解析相应的内部数据结构,然后将资源组织成屏幕上显示的图像,这个过程叫渲染。网页渲染是浏览器最复杂、最核心的功能。

render

浏览器会解析三个东西:

  • HTML/SVG/XHTML,解析这三种文件会产生一个 DOM Tree。

  • CSS,解析 CSS 会产生 CSS 规则树。

  • Javascript脚本,主要是通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree.

解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree,可以理解为“画”元素。

补充:

构建 DOM Tree 的过程中,如果遇到了由 <script> 标签包起来的 js 动态脚本代码,那么会把代码送到 js 引擎里面去跑,如果遇到了 <style>标签包围起来的 css 代码,也会保存下来,用于稍后的渲染。如果遇到了 img 等引用外部文件的标签,那么浏览器会根据指定的 url 再次发起一个新的 HTTP 请求,去把这个文件拉取回来。

6. 关闭TCP连接

通过四次挥手关闭连接:

第一次挥手:浏览器发完数据后,发送 FIN 请求断开连接。

第二次挥手:服务器收到这个FIN,发回一个 ACK 表示同意。

第三次挥手:服务器关闭浏览器的连接,发送一个 FIN 给浏览器。

第四次挥手:浏览器发回 ACK 报文确认。

补充:

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

至此,整个流程基本就完成了,当然,以上只是一些很简单的介绍,还有很多复杂的细节没有提到,有时间我还会继续补充完善。

参考内容