"从浏览器地址栏输入 url 到请求返回发生了什么?",呃,这道面试题我都不知道被问了多少遍,作为必考题,我们有必要总结一下。

总的来说,分为以下几个过程:


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

下面让我们了解下这 5 个过程。

一、DNS 解析

DNS( Domain Name System)是"域名系统"的英文缩写。DNS 解析的过程就是寻找哪台机器上有你需要资源的过程。

当你在浏览器中输入一个地址时,例如​​www.shanzhonglei.com​​,其实不是网站真正意义上的地址。互联网上每一台计算机的唯一标识是它的 IP 地址,但是 IP 地址并不方便记忆,所以就用网址代替 IP 地址。

DNS 域名解析过程


  • 1、浏览器缓存。首先会检查浏览器缓存中有没有域名对应的 ip 地址,这个缓存是有过期时长的,一般是几分钟到几小时不等。
  • 2、本地 hosts 文件。检查自己本地的 hosts 文件是否有这个网址映射关系。windows 就是 C:\Windows\System32\drivers\etc\hosts 文件,linux 在/etc/hosts 文件中配置。
  • 3、路由器缓存。可能还存在路由器缓存这一层。
  • 3、本地 DNS 服务器。如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机。此解析具有权威性。
  • 4、根 DNS 服务器。如果未用转发模式,本地 DNS 就把请求发至 13 台根 DNS 进行解析。如果是转发模式,DNS 服务器就会把请求转发至上一级 DNS 服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根 DNS 或把转请求转至上上级,以此循环。
  • 5、此外还会去查询顶级域名服务器和主域名服务器

所以解析过程大概为:​​浏览器缓存->本地hosts文件->路由器缓存->本地DNS服务器->根DNS服务器->顶级域名服务器->主域名服务器​

前端 DNS 优化

可以在 html 页面头部写入 dns 缓存地址。

<meta http-equiv="x-dns-prefetch-control" content="on" />
<link rel="dns-prefetch" href="http://www.shanzhonglei.com/" />

DNS 负载均衡

如果 DNS 返回的 IP 地址每次都一样,那么这台机器需要多高的性能和储存才能满足亿万请求呢?

其实 DNS 可以返回一个合适的机器的 IP 给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,这种过程就是 DNS 负载均衡,又叫做 DNS 重定向。

大家耳熟能详的 CDN(Content Delivery Network)就是利用 DNS 的重定向技术,DNS 服务器会返回一个跟用户最接近的点的 IP 地址给用户,CDN 节点的服务器负责响应用户的请求,提供所需的内容。

二、TCP 连接

用户数据包协议(User Datagram Protocol),简称 UDP,是基于 IP 之上开发能和应用打交道的协议。

UDP 中一个最重要的信息是端口号,端口号其实就是一个数字,每个想访问网络的程序都需要绑定一个端口号。

通过端口号 UDP 就能把指定的数据包发送给指定的程序了,所以通过 IP 地址信息把数据包发送给指定的电脑,而 UDP 通过端口号把数据包分发给正确的程序。

TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP 相对于 UDP 有两个特点:


  • 对于数据包的丢失,建立重传机制
  • TCP 引入数据包排序机制,用来保证把乱序的数据包组合成一个完整的文件

三次握手,就是指在建立一个 TCP 连接时,客户端和服务器总共要发送 3 个数据包来确认连接的建立。三次握手主要作用:


  • 1、确认双方的接收能力和发送能力
  • 2、指定自己的初始化序列号,为后面的可靠性做准备

2.1 三次握手过程

刚开始客户端处于 Closed 的状态,服务器处于 Listen 状态。


  1. 客户端发送到服务器​。客户端发送 ​​SYN​​ 报文给服务器,并且指明客户端初始化序列号为 ​​ISN(c)​​,即以 ​​SYN=1, seq=x​​ 的形式发送过去。此时客户端处于 ​​SYN_SEND​​ 状态。
  2. 服务器发送给客户端​。服务器收到客户端的 ​​SYN​​ 和 ​​ISN(c)​​,也发送一个 ​​SYN​​ 回去,同时设置 ​​ACK = ISN(c) + 1​​ 以及指明服务器初始化序列号为 ​​ISN(s)​​,即以 ​​SYN=1, ACK=x+1, seq=y​​ 的形式发送给客户端。
  3. 客户端发送到服务器​。客户端收到服务器发送的消息后,设置 ​​ACK = ISN(s) + 1​​,将自身的 ​​ISN(c) + 1​​,即以 ​​ACK=y+1, seq=x+1​​ 的形式发送给服务器。此时客户端处于 ​​ESTABLISHED​​ 阶段,服务器收到报文,也处于 ​​ESTABLISHED​​ 阶段,双方建立了连接。

从输入URL到页面展示,这中间发生了什么_数据 tcp.jpg



2.2、两次握手不行吗?

三次握手的目的:


  1. 客户端发送数据给服务器,服务器确认自己可以接受客户端的请求。
  2. 服务器发送数据给客户端,客户端确认自己可以发送数据给服务器,也可以接受到服务器的请求。
  3. 客户端发送数据给服务器,服务器确认自己可以发送数据给客户端。

如果采用两次握手,客户端发送数据给服务器,服务器确认后就当连接开始:


  1. 客户端发送一次请求给服务器,指定时间后没响应再发了一个
  2. 服务器先接收到后一个建立连接的请求,然后前一个建立连接的请求,因为网络延迟等问题,在第二个之后达到了
  3. 服务器认为第二个请求是最新发的,于是向客户端发送确认报文段,同意建立连接,于是连接建立了(两次握手)
  4. 这时候客户端还在等待最新的请求连接(第二次请求),自动忽略服务器发送的关于第一个请求连接的响应,也不发送数据
  5. 服务器一直等待客户端发送数据,服务器资源被占用

2.3 三次握手过程中可以携带数据吗?

第三次握手的时候可以携带,第一第二次不可以携带。

第三次握手的时候,客户端处于 ESTABLISHED 状态了,它可以建立连接并且知道服务器的接收、发送能力是正常的,所以可以携带数据了。

2.3、四次挥手过程


  • 客户端发送给服务器。客户端以 FIN=1, seq=u 的形式发送给服务器,表示需要关闭客户端和服务器的数据传输。此时客户端处于 FIN_WAIT 状态。
  • 服务器发送给客户端。服务器收到信息,先返回 ACK 给客户端,即以 ACK=1, seq=v, ack=u+1 的形式返回给客户端,表示收到客户端报文了。此时服务器处于 CLOST_WAIT 状态。
  • 服务器发送给客户端。服务器等待一会,看客户端还有没有数据没发过来,等处理完这些数据之后,也想断开连接了,于是发送 FIN 给客户端,即以 FIN=1, ACK=1, seq=w, ack=u+1 的形式发送给客户端。此时服务器处于 LAST_ACK 状态。

客户端发送给服务器。客户端收到 FIN 之后,返回 ACK 报文作为应答,即以 ACK=1, seq=w+1 的形式发送给服务器。此时客户端处于 TIME_WAIT 状态。


从输入URL到页面展示,这中间发生了什么_客户端_02 tcp2.jpg



2.4、为什么需要四次挥手

因为服务器接收到客户端的关闭请求之后。

如果有一些数据因为网络延迟等问题没有发送到,那么它直接关闭会导致这些数据没有接收到;亦或者服务器也有一些数据要发送给客户端,要确保这些数据发送完。

我们知道 ​​TCP​​ 是个可靠的棒小伙,所以它才会第一次回复客户端收到关闭连接的请求了,第二次回复客户端你发送的数据应该没延迟了,我也发送完我要发送的数据了,可以关闭了。

最后客户端接收到了,回复告诉服务器它也可以关闭了,然后过一阵子确保服务器接收到它发的 ​​ACK​​​ 报文之后,也处于 ​​CLOSED​​ 状态了。这样就确保了连接的正常关闭。

三、发送 HTTP 请求

发送 HTTP 请求的过程就是构建 HTTP 请求报文,并通过 TCP 协议发送到服务器指定端口(HTTP 协议默认端口 80/8080,HTTPS 协议默认端口 443)。

HTTP 请求报文由 3 部分组成:请求行、请求报文 和 请求正文。


  • 请求行:常用方法有:GET、POST、PUT、DELETE、OPTIONS、HEAD。
  • 请求报头:允许客户端向服务器传递请求的附加信息和客户端自身的信息。
  • 请求正文:通过 POST、PUT 等方法时,通常需要客户端向服务器传递数据,这些数据就储存在请求正文中。

注意,如果本地缓存是否缓存了该资源,就会直接渲染页面。

GET 和 POST 方法到底有什么区别?

如果没有前提,也就是不用任何规范限制的话,我们只考虑语法来说,GET 请求和 POST 请求都能拉取数据。这两个方式是没有任何区别的,只有名字不一样。

RFC 是一种网络规范,如果基于 RFC 规范,那就不一样:


  • 1、GET 的数据在 URL 中对所有人都是可见的。POST 的数据不会显示在 URL 中。
  • 2、GET 对数据长度有限制,当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。POST 无限制。
  • 3、GET 可收藏为书签,POST 不可收藏为书签。
  • 4、GET 后退按钮/刷新无影响,POST 数据会被重新提交。
  • 5、GET 编码类型 application/x-www-form-url,POST 编码类型 encodedapplication/x-www-form-urlencoded 或 multipart/form-data。为二进制数据使用多重编码。
  • 6、GET 历史参数会保留在浏览器历史中。POST 参数不会保存在浏览器历史中。
  • 7、GET 只允许 ASCII 字符。POST 没有限制。也允许二进制数据。
  • 8、与 POST 相比,GET 的安全性较差,因为所发送的数据是 URL 的一部分。在发送密码或其他敏感信息时绝不要使用 GET !POST 比 GET 更安全,因为参数不会被保存在浏览器历史或 web 服务器日志中。
  • 9、比如 GET 请求只会有一次 TCP 连接,而 POST 请求会有两次 TCP 连接。对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据);而对于 POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。

四种常见的 POST 提交数据方式:


  • 1.application/x-www-form-urlencoded(表单默认方式)
  • 2.multipart/form-data(表单上传文件)
  • 3.application/json
  • 4.text/xml

四、服务器处理请求并返回 HTTP 报文

这一步,会检查状态码,如果是 301/302,则需要重定向,从 Location 自动中读取地址,重新发起请求。如果是 200 状态码,检查响应类型 Content-Type,如果是字节流类型,则将该请求提交给下载管理器,该导航流程结束,不再进行后续的渲染。

如果是 html 则通知浏览器进程准备渲染进程准备进行渲染。

HTTP 响应报文也是由 3 部分组成:状态码、响应报头 和 响应报文。


  • 状态码:
    1xx:指示信息–表示请求已接收,继续处理。
    2xx:成功–表示请求已被成功接收、理解、接受。
    3xx:重定向–要完成请求必须进行更进一步的操作。
    4xx:客户端错误–请求有语法错误或请求无法实现。
    5xx:服务器端错误–服务器未能实现合法的请求。

平时遇到比较常见的状态码有:200, 204, 301, 302, 304, 400, 401, 403, 404, 422, 500(分别表示什么请自行查找)。


  • 响应报头:常见的响应报头字段 Server、Connection 等。
  • 响应报文:服务器返回给浏览器的文本信息,通常 HTML、CSS、JS、图片等文件就放在这一部分。

状态码 301 和 302 的区别?

301 重定向是一种永久重定向,而 302 跳转是暂时的跳转。

五、浏览器解析渲染页面

从输入URL到页面展示,这中间发生了什么_数据_03 cssRender.png


浏览器的渲染过程为:


  1. 解析 HTML,生成 ​​DOM​​ 树
  2. 解析 CSS,生成 ​​CSS 规则树(CSS Rule Tree)​
  3. 将 ​​DOM Tree​​​ 和 ​​CSS Rule Tree​​ 相结合,生成 ​渲染树​(​​Render Tree​​)
  4. 从根节点开始,计算每一个元素的大小、位置,给出每个节点所应该出现的屏幕精确坐标,从而得到基于渲染树的 ​布局渲染树​(​​Layout of the render tree​​)。
  5. 遍历渲染树,将每个节点用 ​​UI​​ 渲染引擎来绘制,从而将整棵树绘制到页面上,这个步骤叫 ​绘制渲染树​(​​Painting the render tree​​)

浏览器在解析过程中,如果遇到请求外部资源时,如图像,JS 等,还会重新执行网络请求。这个请求过程是异步的,并不会影响 HTML 文档进行加载,但是当文档加载过程中遇到 JS 文件,HTML 文档会挂起渲染过程,不仅要等到文档中 JS 文件加载完毕还要等待解析执行完毕,才会继续 HTML 的渲染过程。原因是因为 JS 有可能修改 DOM 结构,这就是 JS 阻塞后续资源下载的根本原因。

CSS 文件的加载不影响 JS 文件的加载,但是却影响 JS 文件的执行。JS 代码执行前浏览器必须保证 CSS 文件已经下载并加载完毕。

建议将 script 标签放到 body 标签底部,或者给 script 标签添加 defer/async 属性。

页面渲染层优化


  • 1、HTML 文档结构层次尽量少,最好不深于六层
  • 2、脚本尽量后放
  • 3、少量首屏样式内联放在标签内
  • 4、样式结构层次尽量简单
  • 5、在脚本中尽量减少 DOM 操作,尽量缓存访问 DOM 的样式信息,避免过度触发回流
  • 6、减少通过 JavaScript 代码修改元素样式,尽量使用修改 class 名方式操作样式或动画
  • 7、动画尽量使用在绝对定位或固定定位的元素上

扩展

DNS 域名称和组织类型

DNS 域名称

组织类型

com

商业公司

edu

教育机构

net

网络公司

gov

非军事政府机构

Mil

军事政府机构

TCP 和 UDP 的区别


  • ​TCP​​​ 是面向连接的,​​UDP​​ 是无连接的即发送数据前不需要先建立链接。
  • ​TCP​​​ 提供可靠的服务。通过 ​​TCP​​​ 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付。并且因为 ​​TCP​​ 可靠,面向连接,不会丢失数据因此适合大数据量的交换。
  • ​TCP​​​ 只能是 1 对 1 的,​​UDP​​ 支持 1 对 1,1 对多。
  • ​TCP​​​ 是面向连接的可靠性传输,而 ​​UDP​​ 是不可靠的。

最后

公粽号【前端技术驿站】,多多交流,共同进步!

回复​​react​​:

1、React.js大众点评案例完整版

2、React+TypeScript高仿AntDesign开发企业级UI组件库

3、React17+React Hook+TS4最佳实践 仿Jira企业级项目

回复​​vue​

1、[全栈开发 ]Vue+Django REST framework 打造生鲜电商项目

2、核心源码内参

3、Vue3+ElementPlus+Koa2 全栈开发后台系统

4、ES6零基础教学解析彩票

5、Node.js+Koa2框架生态实战 - 从零模拟新浪微博(完整版)

6、vue无人点餐收银系统

回复​​node​

1、Nodejs视频教程

2、全栈最后一公里 - Nodejs 项目的线上服务器部署与发布

3、深入浅出Node.js