# 简单流程
从输入 url 到页面呈现会经过几个步骤:DNS 解析 -> TCP 连接 -> 发送 HTTP 请求 -> 服务器处理请求并返回 -> 浏览器解析渲染页面 -> TCP 断开连接
# URL
- URL(Uniform Resource Locator),统一资源定位符,用于定位互联网上资源,俗称网址
- 一个基本的URL由以下几部分组成
- 协议部分(protocol):常见的协议有 http、https、ftp、file,其中最常见的类型是 http,而 https 则是进行加密的网络传输
- 域名部分(domain):该 URL 的域名部分为 www.aspxfans.com。一个 URL 中,也可以使用 IP 地址作为域名使用,www 也是 http 的默认主机(host)
- 端口部分(port):跟在域名后面的是端口,域名和端口之间使用
:
作为分隔符,端口省略则默认为80 - 目录部分(pathname):定义服务器上的路径(如果省略,则文档必须位于网站的根目录中)
- 参数部分(search):从
?
开始到#
为止之间的部分为参数部分,又称搜索部分、查询部分,参数可以允许有多个参数,参数与参数之间用&
作为分隔符。
# 域名解析(DNS)
在浏览器输入网址后,首先要经过域名解析,因为不可能直接通过域名找到对应的服务器,而是要通过 IP 地址
# 什么是域名解析
DNS 协议提供通过域名查找 IP 地址,或者逆向从 IP 地址反查询域名的服务。DNS 是一个网络服务器,我们的域名解析简单来说就是在 DNS 上记录一条信息 例如:
baidu.com 220.114.23.56(服务器外网 IP 地址)80(服务器端口号)
# 浏览器如何通过域名去查询 URL 对应的 IP
浏览器缓存:浏览器会按照一定的频率缓存 DNS 记录(缓存时间比较短,大概只有 1 分钟,且只能容纳 1000 条缓存)
- chrome 的 dns 缓存:
chrome://net-internals/#dns
- chrome 的 dns 缓存:
操作系统缓存:如果浏览器缓存中找不到需要的 DNS 记录,那就去操作系统中找。
- 如果在 Windows 系统的DNS 缓存也没有找到,那么尝试读取hosts文件
- 介绍下hosts:
- 为了提高对经常访问的网络域名的解析效率,可以通过利用 hosts 文件中建立域名和 IP 的映射关系来达到目的
- 在进行 DNS 请求以前,Windows 系统会先检查自己 hosts 文件中是否有这个网路域名映射关系,hosts 文件请求级别高于 DNS
- hosts 文件位置:
C:\Windows\System32\drivers\etc
路由缓存:路由器中也有 DNS 缓存
ISP的DNS服务器:ISP是互联网服务提供商(Internet Service Provider)的简称,ISP有专门的DNS服务器应对DNS请求
根服务器:ISP的DNS还找不到的话,就会向根服务器发起请求,进行递归查询(DNS 服务器先问根域名服务器
主域(com)
名服务器的 IP 地址,然后在问子域
的 IP 地址)浏览器通过向DNS服务器发送域名,DNS服务器查询到与域名相对应的IP地址,然后返回给浏览器,浏览器再将 IP 地址打在协议上,同时请求参数也会在协议搭载,然后一并发送给服务器
# TCP 三次握手
在客户端发送数据之前会发起 TCP 三次握手用以同步客户端和服务端的序列号和确认号,并交换 TCP 窗口大小信息。
- 客户端发送一个带SYN=1,Seq=X的数据包到服务器(第一次握手,由浏览器发起,告诉服务器我要发送请求了)
- 服务器返回一个带SYN=1,ACK=X+1,Seq=Y的响应以表示确认信息(第二次握手,由服务器发起,告诉浏览器我准备接收了,你赶紧发送吧)
- 客户端再回传一个带ACK=Y+1,Seq=Z的数据包,代表握手结束(第三次握手,由浏览器发送,告诉服务器,我马上就发送了,准备接收吧)
- 为什么要三次握手?为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
- 客户端首先发送一个连接试探,ACK=0 表示确认号无效,SYN = 1 表示这是一个连接请求或连接接受报文,同时表示这个数据报不能携带数据,seq = x 表示客户端自己的初始序号(seq = 0 就代表这是第 0 号帧),这时候客户端进入 syn_sent 状态,表示客户端等待服务器的回复
- 服务器监听到连接请求报文后,如同意建立连接,则向客户端发送确认。TCP 报文首部中的 SYN 和 ACK 都置 1 ,ack = x + 1 表示期望收到对方下一个报文段的第一个数据字节序号是 x+1,同时表明 x 为止的所有数据都已正确收到(ack=1 其实是 ack=0+1,也就是期望客户端的第 1 个帧),seq = y 表示服务器自己的初始序号(seq=0 就代表这是服务器这边发出的第 0 号帧)。这时服务器进入 syn_rcvd,表示服务器已经收到客户端的连接请求,等待客户端的确认。
- 客户端收到确认后还需再次发送确认,同时携带要发送给服务器的数据。ACK 置 1 表示确认号 ack= y + 1 有效(代表期望收到服务器的第 1 个帧),客户端自己的序号 seq= x + 1(表示这就是我的第 1 个帧,相对于第 0 个帧来说的),一旦收到客户端的确认之后,这个 TCP 连接就进入 Established 状态,就可以发起 http 请求了。
# 发送 HTTP 请求
TCP 三次握手结束后,开始发送HTTP请求报文 请求报文由请求行、请求头、请求体三部分组成
- 请求行包含请求方法、url、协议版本
- 请求方法包含 8 中:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE
- url 就是请求地址:由 <协议>://<主机>:<端口>/<路径>?<参数> 组成
- 协议版本就是 http 版本号
- 请求头包含附加信息,由键值对组成,每行一对
- 请求体,就是前端的请求参数,不是所有的请求都具有请求数据的
# 服务器处理请求并返回 HTTP 报文
- 响应报文由响应行、响应头、响应主体三个部分组成
- 响应行包括:协议版本、状态码、状态描述
- 状态码规则:
- 1xx:指示信息--表示请求已接收,继续处理
- 2xx:成功--表示请求已被成功接收、理解、接受
- 3xx:重定向--要完成请求必须进行更进一步的操作
- 4xx:客户端错误--请求有语法错误或请求无法实现
- 5xx:服务器端错误--服务器未能实现合法的请求
- 响应头包含响应报文的附加信息,由键值对组成
- 响应体就是后台返回的数据,并不是所有响应报文都有响应数据
- 服务器端 WEB 程序接收到 http 请求以后,就开始处理该请求,处理之后就返回给浏览器 html 文件。
# 浏览器解析渲染页面
浏览器拿到响应后来的 HTML 后,就开始解析其中的 html 代码,浏览器解析渲染页面分为一下五个步骤:
- 根据 HTML 解析出 DOM 树
- 根据 CSS 解析生成 CSS 规则树
- 根据 DOM 树和 CSS 规则树生成渲染树
- 根据渲染树计算每个节点的信息
- 根据计算好的信息绘制页面
# 根据 HTML 解析出 DOM 树
- 根据 HTML 的内容,讲标签按照结构解析成为 DOM 树,DOM 树解析的过程是一个深度优先遍历。即构建完当前节点的所有子节点,在构建下一个兄弟节点
- 在读取 HTML 文档,构建 DOM 树的过程中,若遇到script标签,则 DOM 树的构建就会暂停,直到脚本执行完毕
# 根据 CSS 解析生成 CSS 规则树
- 解析 CSS 规则树时,js的执行将暂停,直至 CSS 规则树就绪。查看细节
- 浏览器就 CSS 规则树生成之前不渲染
# 根据 DOM 树和 CSS 规则树生成渲染树
- DOM 树和 CSS 规则树全部准备好了以后,浏览器才会开始构建渲染树。
- 精简 CSS 并可以加快 CSS 规则树的构建,从而加快页面相应速度。
# 根据渲染树计算每一个节点的信息
- 布局:通过渲染树中渲染对象的信息,计算出每一个渲染对象的位置和尺寸
- 回流:在布局完成后,发现了某个部分发生了变化影响了布局,那就需要倒回去重新渲染。
# 根据计算好的信息绘制页面
- 绘制阶段,系统会遍历呈现树,并调用呈现器的“paint”方法,将呈现器的内容显示在屏幕上。
- 重绘:某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的重绘。
- 回流:某个元素的尺寸发生了变化,则需重新计算渲染树,重新渲染。
# 断开连接
当数据传送完毕,需要断开 tcp 连接,此时发起 tcp 四次挥手。
- 发起方向被动方发送报文,Fin、Ack、Seq,表示已经没有数据传输了。并进入 FIN_WAIT_1 状态。(第一次挥手:由浏览器发起的,发送给服务器,我请求报文发送完了,你准备关闭吧)
- 被动方发送报文,Ack、Seq,表示同意关闭请求。此时主机发起方进入 FIN_WAIT_2 状态。(第二次挥手:由服务器发起的,告诉浏览器,我请求报文接受完了,我准备关闭了,你也准备吧)
- 被动方向发起方发送报文段,Fin、Ack、Seq,请求关闭连接。并进入 LAST_ACK 状态。(第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完了,你准备关闭吧)
- 发起方向被动方发送报文段,Ack、Seq。然后进入等待 TIME_WAIT 状态。被动方收到发起方的报文段以后关闭连接。发起方等待一定时间未收到回复,则正常关闭。(第四次挥手:由浏览器发起,告诉服务器,我响应报文接受完了,我准备关闭了,你也准备吧)
- 断开连接端可以是客户端,也可以是服务端。假设客户端发起中断连接请求:
- 第一次挥手:客户端先发送 FIN 报文(第 24 帧),用来关闭主动方到被动关闭方的数据传送,也就是客户端告诉服务器:我已经不会再给你发数据了(当然,在 fin 包之前发送出去的数据,如果没有收到对应的 ack 确认报文,客户端依然会重发这些数据),但此时客户端还可以接受数据。
- 第二次挥手:服务端接到 FIN 报文后,但是如果还有数据没有发送完成,则不必急着关闭 Socket,可以继续发送数据。所以服务器端先发送 ACK(第 25 帧),告诉客户端:请求已经收到了,但是我还没准备好,请继续等待停止的消息。这个时候客户端就进入 FIN_WAIT 状态,继续等待服务端的 FIN 报文。
- 第三次挥手:当服务端确定数据已发送完成,则向客户端发送 FIN 报文(第 26 帧),告诉客户端:服务器这边数据发完了,准备好关闭连接了。
- 第四次挥手:客户端收到 FIN 报文后,就知道可以关闭连接了,但是他还是不相信网络,所以发送 ACK 后进入 TIME_WAIT 状态(第 27 帧), 服务端收到 ACK 后,就知道可以断开连接了。客户端等待了 2MSL 后依然没有收到回复,则证明服务端已正常关闭,最后,客户端也可以关闭连接了至此,TCP 连接就已经完全关闭了!