从输入 URL 到页面展示,这中间发生了什么?

  • HTTP
  • 梁凤波
  • 523
  • 14

概述

  • 首先开启网络进程,检测缓存资源
  • 第一步:DNS解析
  • 第二步:TCP/IP连接
  • 第三步:HTTP请求/响应
  • 第四步:资源加载渲染

首先开启网络进程,检测缓存资源

首先会开启一个网络进程,网络进程接收到一个 URL 请求后,网络进程会进行查找本地缓存是否缓存了改资源,如果有缓存资源,那么直接返回资源给浏览器进程;如果在缓存中没有查找到资源,那么直接进入网络请求流程。

第一步:DNS解析

第一步会进行 DNS 解析,解析之前会进行 DNS 缓存查询,查询的步骤分为:

浏览器缓存 ——> 系统缓存 ——> hosts文件 ——> 路由器缓存 —— ISP(互联网服务提供商)DNS缓存

如果查询不到的话,客户端就会向 DNS 服务器发起 DNS 解析请求,DNS 解析分为2个步骤,分别是递归查询,和迭代查询:

  • 递归查询指:递归查询是一种DNS 服务器的查询模式,在该模式下DNS 服务器接收到客户机请求,必须使用一个准确的查询结果回复客户机。如果DNS 服务器本地没有存储查询DNS 信息,那么该服务器会询问其他服务器,并将返回的查询结果提交给客户机。
  • 迭代查询指:DNS 服务器另外一种查询方式为迭代查询,DNS 服务器会向客户机提供其他能够解析查询请求的DNS 服务器地址,当客户机发送查询请求时,DNS 服务器并不直接回复查询结果,而是告诉客户机另一台DNS 服务器地址,客户机再向这台DNS 服务器提交请求,依次循环直到返回查询的结果

递归查询与迭代查询示意图:

  [递归查询]
   客户端    

   |    ^
   |    |
  查询 回复
   |    |
   v    |
                 —— 查询 ——>  DNS根服务器  
                 <—— 回复 ——  DNS根服务器  

 [迭代查询]       —— 查询 ——> .com服务器
本地DNS服务器     <—— 回复 —— .com服务器

                 —— 查询 ——> boblog.com服务器
                 <—— 回复 —— boblog.com服务器

递归查询与迭代查询流程大白话:

下面的「老师」代表客户端,「你」代表本地 DNS 服务器。

// 递归查询
「老师」问「你」知道小红家里的地址是多少吗?
// 迭代查询
「你」不知道,马上跑去问「小黄」:请问你知道小红家里的地址是多少吗?
「小黄」说:他也不知道,但小和应该知道,你去找「小和」吧

「你」又跑来问「小和」:请问你知道小红家里的地址是多少吗?
「小和」说:他也不知道,但小波应该知道,你去找「小波」吧

「你」又跑来问「小波」:请问你知道小红家里的地址是多少吗?
噢!「小波」真知道小红家的地址,马上告诉你「小红」的地址了
// 递归查询
「你」知道了小红家的地址后,也告诉告诉了「老师」了

注意的一点:如果经过 DNS 解析最终都没用得到 IP 的话,会报:「找不到 xxx.com 的服务器 IP 地址。DNS_PROBE_FINISHED_NXDOMAIN」错误,如图下显示:

img

最后本地 DNS 服务器拿到 IP 后缓存起来且返回给客户端IP地址,如果请求协议是 HTTPS,那么还需要建立 TLS 连接。DNS 解析是一个很耗时的查询,可以进行设置 DNS 缓存和 DNS 预解析优化。


<meta http-equiv="x-dns-prefetch-control" content="on">

<link rel="dns-prefetch" href="//boblog.com">

Tips: DNS Prefetch 解析后的域名对应关系会缓存到本地,一般本地缓存的数量为 50~200 个。建议配置使用比较频繁的域名这里一般是指存放图片资源的服务器域名和一些经常发送Ajax请求会用到的域名。

第二步:TCP/IP连接

拿到IP后就开始进行TCP连接,TCP连接需要经过三次握手才能确认连接,目的是确保数据能够安全到达送达。

三次握手大白话的经过:

  • 客户端:朋友,你有快递到了,你现在方便签收快递吗?
  • 服务端:收到,我现在在家,麻烦你现在送过来吧!
  • 客户端:好的,马上给你送过去!

第三步:HTTP请求/响应

确认连接后,就开始利用 TCP/IP 协议族进行网络通信,客户端从应用层发起了一个 HTTP 请求,HTTP 请求包含 HTTP 请求报文,包含请求头,请求体,比如请求方法有 GET, POST 方法,采取 HTTP1.1 协议。

为了方便传输,在传输层 TCP 协议把从应用层接收到的 HTTP 请求报文进行分割,并且在各个报文上打上标记序号和端口号转发给网络层。

在网络层 IP 协议,增加作为通信目的地的 MAC 地址后转发给链路层。这样客户端发送的请求接收,到了服务端后开始从链路层开始接收到数据,按顺序一层层发送到应用层,在应用层开始解析 HTTP 的请求头和请求体。

如果需要重定向,HTTP直接返回数据的状态码 301 或者 302,同时在请求头的 Location 字段附上需要重定向的地址,浏览器会根据 Location 进行重定向操作。

如果不需要重定向,服务器根据请求头中的 if-Modified-Since 和 If-None-Match 会验证资源是否需要更新,如果不用更新,返回 304 状态码,协商缓存,相对于告诉浏览器说缓存的资源还没有过期可以继续使用,就不返还新的数据了。如果过期了,则返回新的数据和200状态码,并且想浏览器想更新缓存数据的话,就在相应的头加上 Cache-Control: max-age=[秒],或 Last-modified 新的时间戳或 ETag 新的标识符,最后 HTTP 响应返回数据,响应数据又顺着应用层——传输层——网络层——网络层——传输层——应用层的顺序返回到网络进程,当数据传输完成,TCP需要经过四次挥手断开连接。

四次挥手大白话:

  • 客户端:你好,我这边没有数据需要传输了,我们关闭连接吧!
  • 服务端:好的,我也查查我这边有没有数据需要传输。
  • 服务端:你好,我这边也没有数据需要传输了,我们关闭连接吧!
  • 客户端:好的,再见!

如果浏览器或者服务器头部加上 Connection: Keep-Alive,TCP就会一直保持连接,保持TCP连接可以省下下次连接建立的时间,提升资源加载的速度。从HTTP/1.1起,浏览器默认都开启了Keep-Alive,保持连接特点,但也有超时时间。

第四步:资源加载渲染

浏览器接收到数据包后进行解析,根据响应头中的 Content-type 来判断响应数据的类型,如果是字节流类型,就将该请求交给下载管理器,如果是text/html类型或者其他类型,就通知浏览器进程获取到文档准备渲染。

浏览器会发出“提交文档”的消息给渲染进程,渲染进程收到消息后,会和网络进程建立传输数据的“管道”,文档数据传输完成后,渲染进程会返回“确认提交”的消息给浏览器进程。

浏览器收到“确认提交”的消息后,会更新浏览器的页面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新web页面,此时的web页面是空白页

渲染进程对文档进行页面解析和子资源加载,HTML 通过HTM 解析器转成DOM Tree(二叉树类似结构的东西),CSS按照CSS 规则和CSS解释器转成CSSOM TREE,两个tree结合,形成render tree(不包含HTML的具体元素和元素要画的具体位置),通过Layout可以计算出每个元素具体的宽高颜色位置,结合起来,浏览器会将各层的信息发送给GPU,GPU会将各层合成,开始绘制,最后显示在屏幕中新页面显示出来。

为什么要构建 DOM 树呢?这是因为浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。

欢迎评论
评论列表
avatar

Waves

666

avatar

zsda

dda

avatar

csa

sc

avatar

后端入门

不想开发后端的前端,不是好前端。

avatar

cmd

做得真好,膜拜大佬

avatar

码农

真棒
...

来自「xddd」的回复

sdasdas
...

来自「taowa」的回复

sdafds
...

来自「taowa」的回复

dsadsa
avatar

测试小哥

Hello World!
...

来自「测试小弟」的回复

你好
...

来自「ddd」的回复

hhah
...

来自「打算」的回复

阿斯顿飞
...

来自「后端入门」的回复

不想开发后端的前端,不是好前端。
...

来自「差点擦」的回复

cdcd
...

来自「测试南瓜」的回复

ssgsgsg
TOP