该题是一道特别经典的面试题了,涉及到前端人员应该掌握的基础网络知识,相信很多前端同学都了解。但是如果问深了,也能问出很多子问题来,也更能考验出面试者的知识深度和灵活运用程度。
浏览器输入URL之后大致有这么几个阶段:
- DNS域名解析
- 建立TCP连接
- 发送HTTP请求
- 服务端处理请求
- 服务端返回响应结果
- 关闭TCP连接
- 浏览器解析HTML并渲染布局
1. DNS域名解析
- 我们访问一个网站的时候,可以通过
主机名或者域名
来访问。但是绝大多数的时候是使用域名来访问
网站。因为相对于主机名的ip地址
,域名更能让人记住。 - 但是
TCP/IP协议是通过IP地址来访问
的,所以就需要一个机制
,将域名转换为IP地址
- 而DNS服务就是做这件事情,它提供了
域名
到IP地址
之间的解析服务
DNS解析过程
- 1 . 访问
www.baidu.com
到DNS
服务器- 2 .
DNS
服务器返回www.baidu.com
的IP地址115.182.4X.18X
- 3 . 接着请求IP地址为
115.182.4X.18X
的服务器- 4 . 成功访问到
www.baidu.com
的真正服务器DNS的优先级
本地电脑会将一些
经常使用的域名和对应的IP地址
建立一个映射关系,并保存到本地host
文件中。当DNS解析的时候,会优先从本地host
文件中查找映射的IP地址
映射
- 1 . 如果在本地
host
文件中找到了域名对应的IP
地址映射,会直接使用host
文件中的IP地址- 2 . 如果在本地
host
文件中没有找到域名对应的IP
地址映射,会从本地DNS服务器
中查找- 3 . 如果在
本地DNS服务器
中也没有找到域名对应的IP
地址映射,会继续向上一级的DNS服务器
发送请求,直到DNS根服务器
。如果找到,就进行回传
,返回给浏览器。
2. 建立TCP链接
先了解一个知识点: TCP/IP协议族
TCP/IP协议族是由四层协议
组成的系统。分别是:
- 1 . 应用层(http)
- 2 . 传输层(tcp)
- 3 . 网络层(ip)
- 4 . 链路层(网络硬件)
我的理解是:我们访问一个网站之前,首先要保证有网络
对吧?有网线 / 路由器对吧?那么此时,链路层
就是ok的。经过上面第一步,已经知道了该域名映射的IP地址
,并且可以访问,那么此时网络层也是ok
的。
那么接下来,就是传输层,也就是TCP
。其实这里说的也不是很严谨,传输层不仅仅是TCP
一种协议,还有UDP
协议。UDP
协议是无连接的
,因为其不需要连接,所以效率比较高
,但是也是因为不需要连接验证 所以安全性和可靠性得不到保障
。而TCP是面向连接
的,有校验机制。所以用的较广泛。但是也有缺点: 因为提前建立了连接,所以效率就较低
。本文说的传输层指的是TCP协议
。
为了确保连接双方的可靠性
,在双方建立连接时,TCP采用了三次握手策略
TCP三次握手建立连接
- 1 . 第一次握手
客户端发送带有
SYN标志
的连接请求报文段
,然后客户端就进入了SYN_SEND状态
等待服务器确认
- 2 . 第二次握手
服务端接收到了客户端浏览器发送的
SYN标志
报文段后,需要发送ACK确认报文段
对这个SYN报文段
进行确认。同时又会向客户端发生自己的SYN
请求信息。服务端会将上述信息放到一个报文段(SYN+ACK报文段)中
一同发送给客户端。此时服务端进入SYN_RECV
阶段
- 第三次握手
客户端浏览器接收到了服务端返回的
SYN+ACK报文段(请求+返回)
后,会向服务器发送新的ACK确认报文段
。该报文段发送完毕后,客户端和服务端都进入到了ESTAB-LISHED
(建立连接)状态,此时 就完成了三次握手
知其然更要知其所以然,
为什么要进行三次握手才能建立TCP连接?
根本原因就是
为了确保客户端和服务端双方的接收和发送能力都是ok的
。
经过第一次握手后,服务端收到了客户端的SYN
请求标志,那么此时服务端就可以知道:客户端的发送能力是ok的,服务端本身自己的接收能力是ok的
。
经过第二次握手后,客户端收到了服务端的SYN+ACK
请求+返回标志,那么此时,客户端就可以知道:客户端本身的发送和接收能力是ok的,服务端的发送和接收能力有是ok的
。
经过第三次握手后,服务端接收到了客户端的ACK
返回标志,那么此时,服务端就可以知道:服务端本身自己的接收和发送能力是ok的
。
3. 发送HTTP请求
建立TCP连接之后,就可以发送HTTP请求了。说到HTTP请求,那么前端同学就不得不了解下关于HTTP报文
的相关知识点。
HTTP报文的组成部分
- 1 . 请求报文
请求行:请求方法/页面地址/协议及版本
请求头:报文头 key/value值
空行:作为请求头和请求体的分割符
请求体:报文体 传递参数等
- 2 . 响应报文
响应行:协议及版本/状态描述
响应头:报文头 key/value值
空行:作为响应头和响应体的分割符
响应体:报文体 返回响应结果
这样看不怎么直观,我们可以通过curl
(命令行网络测试工具)来测试发起请求的报文结构。
打开终端,输入curl -v www.baidu.com
,可以看到:
上方截图的请求报文
旁边空白比较少,单独拎出来讲一下:
GET / HTTP/1.1 // 请求行 (方法/路径/协议)
// 以下是请求头
Host: www.baidu.com
User-Agent:curl/7.54.0
Accept: */*
// 空行
// 请求体 因为没有任何参数 所以是空的
一些常用的报文头
请求头和响应头
都属于报文头
常用报文头
- 1. Accept:客户端可以接受的媒体类型
-Accept:text/html
: 表示浏览器可以接受服务器返回的类型是tetx/html
。也就是常说的 html 文档 但是如果服务器无法返回text/html
类型的数据,服务器应该返回一个406(Non Acceptable)错误
-Accept:*\*
: 代表浏览器可以处理所有类型
- 如果想要给显示的媒体类型增加优先级 则可以使用q=?
来额外表示权重
。权重q的范围是0~1
,权重 q 默认是 1
- 2. Accept-Encoding: 浏览器申明自己的编码方法
- 通常指定压缩方法 是否支持压缩 支持什么压缩方法(gzip / deflate)
- 3. Accept-Language: 浏览器申明自己的语言
-Accept-Language:zh-cn,zh;q=0.7,en-us,en;q=0.3
- 也可以指明权重信息
- 4. Connection:keep-alive
- 当一个网页打开完成后,客户端和服务端之间用于传输 HTTP 数据的 TCP 连接不会关闭
,注意是TCP 连接
。这样做的好处是避免多次的 3 次握手和 4 次挥手
- Connection:close
- 当一个网页打开完成后,客户端和服务端之间用于传输 HTTP 数据的 TCP 连接会关闭
- 当客户端再次发送请求时,需要重新建立TCP连接
- 5. Host: 被请求资源的 Interent主机名和端口号
- 它通常从 URL 中提取出来
-www.xxx.com:8080
- 6. Referer: 来源
- 用于告知服务器是从哪个链接进来的
,服务器借此可以获得一些信息用于处理
- 7. User-Agent: 告诉服务器 客户端使用的操作系统的名称和版本
- 服务端可以通过User-Agent
来判断浏览器类型,作出不同的兼容处理
- 8. Content-Type: 说明报文体内对象的媒体类型
-text/html
:HTML 格式
-text/plain
:纯文本格式
-text/xml
:XML 格式
-image/gif
:gif 图片格式
-text/jpeg
:jpg 图片格式
-text/png
:png 图片格式
-application/json
:JSON 数据格式
-application/xxml
:XML 数据格式
-application/msword
:Word 文档格式
-application/octet-stream
:二进制数据流(常见于文件下载)
-application/x-www-form-urlencoded
:表单提交
4. 服务端处理请求
服务端接收到客户端的请求后,开始处理请求。调数据库 处理资源等。这里不过多阐述
5. 服务端返回响应结果
有请求就会有响应,即使是错误信息。关于响应报文
上方已经阐述。下面说一下状态码。
状态码
用于表示网页服务器超文本传输协议响应状态的
3位数字代码
状态码首位数字
- 1xx: 指示信息 - 表示请求已接受,但是需要继续处理 临时响应
- 2xx: 成功 - 表示请求已被成功接收
- 3xx: 重定向 - 要完成请求必须进行更进一步的操作
- 4xx: 客户端错误 - 请求有语法错误或请求无法实现
- 5xx: 服务器错误 - 服务器未能实现合法的请求
常用状态码
- 200 OK:客户端请求成功
- 202 Accepted: 已接受 已经接受请求 但是未处理完成
- 206 Partial Content: 断点续传
客户发送了一个带有 Range<范围>头的 GET 请求,服务器完成了 它 <请求视频或者音频文件很大的时候 服务器就会给你返回部分[Range] 的文件
>
- 301 Moved Permanently:所请求的页面已经转移至新的 url <永久重定向
>
- 302 Found:所请求的页面已经临时转移至新的 url <临时重定向
>
- 304 Not Modified: 之前缓存可用
客户端有缓存的文档并发出了一个条件性的请求,服务器告诉客户端,原来缓存的文档还可以继续使用
- 400 Bad Request:客户端请求语法错误,不能被服务器所理解
- 401 Unauthorized:请求未经授权 要求用户的身份认证
,这个状态码必须和 WWW-Authenticate
报头域一起使用
- 403 Forbidden: 服务器理解请求客户端的请求,但是拒绝执行此请求
- 404 Not Found:请求资源不存在
- 500 Internal Server Error:服务器内部错误,无法完成请求
- 502 Bad Gateway: 网关或代理的服务器发生了错误
从远端服务器接收到了一个无效的请求
- 503 Server Unavailable: 请求未完成,服务器临时过载或宕机
,一段时间后可能恢复正常
6. 关闭TCP连接
当客户端和浏览器双方请求和响应完毕后,任意一方都可以发起断开TCP连接的请求。断开TCP连接要通过
四次挥手
来实现。TCP四次挥手断开连接
经过之前的
三次握手
客户端和服务端都进入了ESTAB-LISHED
状态
- 1 . 第一次挥手
客户端发送带有
FIN标志
的断开连接请求报文段
,然后客户端就进入了FIN-WAIT-1状态
(终止等待状态1) 等待服务器确认
- 2 . 第二次挥手
服务端接收到了客户端浏览器发送的
FIN标志
报文段后,需要发送ACK确认报文段
对这个FIN报文段
进行确认,此时服务端进入CLOSE-WAIT状态
(关闭等待状态)。客户端收到该ACK却报文段
后,客户端就进入了FIN-WAIT-2状态
(终止等待状态2)。(此时客户端向服务端的TCP连接已经关闭
。但是服务端向客户端的TCP连接还没有关闭)
- 3 . 第三次挥手
等待服务端已经没有任何数据发送给客户端之后,服务端会向客户端发送
FIN+ACK报文段
(关闭+确认)。然后服务端进入到LAST-ACK状态
(最后确认状态)
- 4 . 第四次挥手
客户端向服务端发送
ACK确认标志
。服务端收到该确认信息后进入到CLOSED状态
(TCP关闭状态)。客户端在等待2MSL(报文最大生存时间)
时间后,也进入到了CLOSED状态
(TCP关闭状态)。此时四次挥手
成功,客户端和服务端双方都已关闭TCP连接
面试中关于四次挥手
经常问到的是为什么最后客户端会等待一段时间之后才会进入关闭状态?
因为最后一次挥手
客户端发送给服务端的确认信息,服务端可能会没有收到。那么如果服务端没有收到客户端发送的最后确认信息,那么服务端会认为我本身发送给客户端的关闭请求 因为客户端没有收到该请求,所以客户端才没有给我发送确认信息,我才会收不到。所以服务端会再次向客户端发送该关闭请求。为了避免客户端真的没有收到服务端的关闭请求,客户端会等待一段时间(报文最大生存时间)之后,再进入关闭状态
7. 浏览器解析HTML并渲染布局
客户端浏览器在收到服务端的响应结果后,就要开始解析并渲染了,
webkit 引擎渲染大致过程:
- 1 .
HTML
经过HTML Parser
转成DOM Tree
(DOM 树)- 2 .
CSS
按照CSS 规则
和CSS Parser
转成CSS Tree
- 3 .
DOM Tree
和CSS Tree
结合形成Render Tree
- 4 . 通过
Layout
精确的计算出要显示的DOM
真正的位置- 5 . 浏览器通过
Paint
显示出最终的页面效果
这其中有几个经常会被问到的面试题
什么是重绘(
Repaint
)?重绘就是
Repaint
,是在一个元素的外观被改变,但没有改变布局
(改变元素的非几何属性
)的情况下发生的,如改变了visibility、outline、background
等。当repaint发生时,浏览器会验证DOM树上所有其他节点的visibility属性。
当各种盒子的位置、大小以及其他属性
,例如颜色、字体大小等都确定下来以后,浏览器便把这些元素都按照各种的特性绘制一遍
,于是页面的内容就出现了,这个过程称之为repaint
如何触发重绘(
Repaint
)?改变元素
非几何属性(外观属性)
。如:color
background-color
等什么是重排(
Reflow
)?
DOM
结构中的各个元素都有自己的盒子(模型)
,这些都需要浏览器根据各种样式来计算并根据计算结果将元素放到它该显示的位置
(改变元素的几何属性
),这个过程称之为reflow
如何触发重排(
Reflow
)?
- 1 .
增加、删除、修改DOM节点时
会导致Reflow
或Repaint
- 2 .
移动DOM的位置
或者DOM动画
的时候- 3 .
修改CSS样式
的时候- 4 .
Resize窗口大小
的时候(移动端没有这个问题),或是滚动的时候
都有可能触发Reflow
- 5 .
修改网页的默认字体
的时候减少 Reflow 对性能的影响的建议?
Reflow(回流)是导致 DOM 脚本执行效率低的关键因素之一
,页面上任何一个节点触发了Reflow
,会导致它的子节点及祖先节点的重新渲染
- 1 . 不要
一条一条地修改DOM的样式
,预先定义好class
,然后修改DOM
的className
- 2 . 把
DOM离线后再修改
,比如:先把DOM
给display:none
(有一次Reflow
),然后修改 100 次后,然后再把它display:block
- 3 . 不要把
DOM节点的属性值放在一个循环里当成循环里的变量
- 4 . 尽可能
不修改影响范围比较大的 DOM
- 5 . 为
动画的元素使用定位 absolute / fixed
- 6 .
慎重选择高消耗的样式
比如有:box-shadows
|border-radius
|transparency
|transforms
|CSS filters(性能杀手)
- 7 .
不要使用 table 布局
,可能很小的一个小改动会造成整个 table的重新布局
重排和重绘的关系?
重排必定会引发重绘,但重绘不一定会引发重排
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。