1. 说一下 GET 和 POST 的区别?
GET
在浏览器回退时是无害的
,而POST
会再次提交
请求GET
请求只能进行url 编码
,而POST
支持多种编码
方式GET
请求参数会被完整保留在浏览器历史记录里,而POST
中的参数不会保留GET
请求参数通过URL 传递
且参数是有长度限制
的(2083 字符,中文字符的话只有 2083/9=231 个字符),而POST
参数放在Request body
中是没有限制的注意:
HTTP 协议
未规定GET
和POST
的长度限制;GET
的最大长度显示是因为浏览器
和web 服务器
限制了URI
的长度。不同的浏览器
和WEB 服务器
,限制的最大长度不一样,要支持IE
,则最大长度为 2083byte,若只支持Chrome
,则最大长度 8182byte对参数的数据类型,
GET
只接受ASCII 字符
(如果非ASCII
字符会进行转码
),而POST
没有限制GET
和POST
本质上就是TCP 连接
,GET
产生 一个TCP 数据包
;POST
产生 两个TCP 数据包
注意:并不是所有浏览器都会在
POST
中发送两次包,Firefox
就只发送一次- 对于
GET
方式的请求,浏览器会把http header
和data
一并发送出去,服务器响应 200(返回数据) - 对于
POST
,浏览器先发送header
,服务器响应100 continue
,浏览器再发送data
,服务器响应 200 ok(返回数据)
- 对于
2. HTTP1.0、HTTP1.1、HTTP2、HTTP3 的区别?
HTTP 1.0(1996 年)
- 任意数据类型都可以发送
- 有
GET
、POST
、HEAD
三种方法 - 无法复用
TCP 连接
(长连接) - 有丰富的请求响应头信息。以
header
中的Last-Modified/If-Modified-Since
和Expires
作为缓存标识
HTTP 1.1(1997 年)
- 引入更多的请求方法类型
PUT
、PATCH
、DELETE
、OPTIONS
、TRACE
、CONNECT
- 引入
长连接
,就是TCP 连接
默认不关闭,可以被多个请求复用,通过请求头connection: keep-alive
设置 - 引入
管道
连接机制,可以在同一TCP
连接里,同时发送多个请求 - 强化了缓存管理和控制
Cache-Control
、ETag/If-None-Match
- 支持
分块响应
,断点续传
,利于大文件传输,能过请求头中的Range
实现
HTTP 2.0(2015 年)
- 使用新的
二进制协议
,不再是纯文本,避免文本歧义,缩小了请求体积 多路复用
,同域名下所有通信都是在单链接(双向数据流)完成,提高连接的复用率,在拥塞控制方面有更好的能力提升- 允许服务端主动推送数据给客户端
- 使用
HPACK
算法将头部压缩
,用哈夫曼编码
建立索表,传送索引大大节约了带宽
HTTP 3.0/QUIC
缺点:主要是连接缓慢,服务器只能按顺序响应,如果某个请求花了很长时间,就会出现请求 队头阻塞
HTTP1 和 HTTP2
HTTP2
是一个二进制协议
,HTTP1
是超文本协议
,传输的内容都不是一样的HTTP2
报头压缩
,可以使用HPACK
进行头部压缩,HTTP1
则不论什么请求都会发送HTTP2
服务端推送(Server push
),允许服务器预先将网页所需要的资源push
到浏览器的内存当中HTTP2
遵循多路复用
,代替同一域名下的内容,只建立一次连接,HTTP1.x
不是,对域名有 6~8 个连接限制
HTTP 请求方法(9 种)
HTTP1.0
: GET
、POST
、HEAD
HTTP1.1
: PUT
、PATCH
、DELETE
、OPTIONS
、TRACE
、CONNECT
HTTP
全称 Hyper Text Transfer Protocol
,即超文本传输协议。HTTP
是一个 应用层协议。
HTTP
的报文结构:请求行
+ 请求头
+ 请求体
Accept 字段与 Content-type 字段:
Accept
: 用于客户端向服务器发送报文时表示自己可接收的响应内容类型,如:Accept: text/plain
(文本类型)Accept-Charset
: 表示可接收的字符集Accept-Encoding
: 表示可接受的响应内容的压缩方式Accept-Language
: 表示可接受的响应内容语言列表Accept-Datetime
: 表示可接受的按照时间来表示的响应内容版本
Content-Type
: 字段用于服务器回应时,告诉客户端,本次数据的格式是什么Content-Encoding
: 表示服务器返回的数据使用什么压缩格式
Host
字段: 用于客户端发送请求时,用来指定服务器的域名。例如:Host: www.baidu.com
Connection
字段: 最常用于客户端要求服务器使用TCP
持久连接,以便其他请求复用keep-alive
Content-Length
字段: 表明本次回应的数据长度
HTTP 存在的问题
性能问题
在
HTTP/1.0
中,每次发起一个HTTP
请求,都需要去建立一次TCP 连接
,而且还是串行请求,这使得HTTP
在TCP 的连接
建立上花费了大量的开销。对于这种问题,HTTP/1.1
中提出了长连接的通信方式,也叫持久连接
。好处: 在于减少了TCP 连接
的重复建立和断开所造成的额外开销,减轻了服务器端的负载HTTP/1.1
采用了长连接
的方式,使得管道(Pipeline)
网络传输成为了可能。即在同一个TCP 连接
里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。但是 服务器还是按照顺序,先回应第一个请求,完成后再回应第二个请求,以此类推。要是前面的请求回应得特别慢,后面就会有许多请求阻塞着,这就是所谓的【队头阻塞】
总结: HTTP/1.0
或是 HTTP/1.1
性能都不是很完美
安全问题
HTTP
的内容是明文传输
的,明文数据会经过中间代理服务器
、路由器
、WIFI热点
、通信服务运行商
等多个物理节点,如果信息在传输过程中被劫持,传输的内容久完全暴露了,劫持者还可以篡改传输的信息且不被双方察觉,这就是 中间人攻击总结一下,
HTTP
在安全方面有以下三个问题:- 使用明文通信,一些重要的内容会被窃听
- 不能验证对方身份,可能是伪造的信息
- 无法验证报文的完整性,有可能被修改
HTTP/2
有哪些改进?
- 头部压缩
- 多路复用
- 服务器推送
- 二进制传输
HTTP 的特点和缺点
特点:无连接 (
请求完就断开,不保持连接
)、无状态 (每个请求都是独立的
)、灵活 (http 协议中头部的 Content-Type 标记,可以传输任意数据类型的数据对象(文本、图像、视频等。比较灵活)
)、简单快速 (请求访问某个资源时,只需传送请求方法和 URL 就可以了,使用简单
)缺点:无状态、不安全 (
明文传输可能被窃听不安全,缺少身份认证也可能遭遇伪装,还有缺少报文完整性验证可能遭到篡改
)、明文传输、队头阻塞 (一个TCP连接,同一时刻只能处理一个请求,那么当请求耗时过长时,其他请求就只能阻塞状态
)什么是 持久连接/长连接?
http1.0
协议采用的是 "请求-应答" 模式,每个请求/应答客户与服务器都要新建一个连接,完成之后立即断开连接(http
协议为无连接的协议)http1.1
版本支持长连接
,即请求头添加Connection: Keep-Alive
,使用Keep-Alive
模式(又称持久连接
,连接复用
)建立一个TCP
连接后使客户端到服务端的连接持续有效,可以发送/接受多个http
请求/响应,当出现对服务器的后续请求时,Keep-Alive
功能避免了建立或者重新建立连接- 长连接优缺点?
- 优点
- 减少 CPU 及内存的使用,因为不需要经常建立和关闭连接
- 支持管道化的请求及响应模式
- 减少网络堵塞,因为减少了 TCP 请求
- 减少了后续请求的响应时间,因为不需要等待建立 TCP、握手、挥手、关闭 TCP 的过程
- 发生错误时,也可在不关闭连接的情况下进行错误提示
- 缺点
- 一个长连接建立后,如果一直保持连接,对服务器来说是多么的浪费资源呀,而且长连接时间的长短,直接影响到服务器的并发数
- 还有就是可能造成
队头堵塞
- 优点
- 如何避免长连接资源浪费?
- 客户端请求头声明:
Connection: close
,本次通信后就关闭连接 - 服务端配置:如
Nginx
,设置keepalive_timeout
设置长连接超时时间
,keepalive_requests
设置长连接请求次数上限
- 客户端请求头声明:
- 长连接优缺点?
什么是 管线化(管道化)?
请求1 -> 响应1 -> 请求2 -> 响应2 -> 请求3 -> 响应3
请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3
管线化: 是在同一个
TCP 连接
里发一个请求后不必等其回来就可以继续发请求出去
,这可以减少整体的响应时间,但是服务器还是会按照请求的顺序响应请求,所以如果有许多请求,而前面的请求响应很慢,就产生一个著名的问题 队头堵塞管线化
的特点:管线化
机制通过持久连接
完成,在http1.1
版本才支持- 只有
GET
请求和HEAD
请求才可以进行管线化,而POST
有所限制 管线化
不会影响响应到来的顺序,响应返回的顺序就是请求的顺序- 初次创建连接时不应启动管线化机制,因为服务器不一定支持
http1.1
版本的协议
如何解决
HTTP
的 队头阻塞 问题?并发连接 (现在的浏览器标准中一个域名并发连接可以有 6~8 个,记住是 6~8 个,不是 6 个(Chrome6 个/Firefox8 个))
域名分片 (一个域名最多可以并发 6~8 个,那咱就多来几个域名,多准备几个二级域名,可以让不同的资源从不同的二域名中获取,而它们都指向同一台服务器,这样能够并发更多的长连接了)
注意:在
HTTP2.0
下,可以一瞬间加载出来很多资源,因为支持多路复用
,可以在一个 TCP 连接中发送多个请求
3. DNS 解析过程及原理?回源是什么?DNS 劫持听说过吗?
解析域名的查找过程:
浏览器的 DNS 缓存 -> 操作系统的 DNS 缓存 -> 本地域名服务器(递归查询 自己的 DNS 缓存) -> 上级域名服务器(进行迭代查询)【本地域名服务器 -> 根域名服务器(会返回顶级域名的服务器地址); 本地域名服务器 -> 顶级域名服务器(返回权威域名服务器地址,全球 13 台) 】 -》解析到的 IP 地址返回给操作系统,并缓存起来。-》操作系统将 IP 地址返回给浏览器,并缓存起来,供下次使用。
DNS 劫持,是指通过 攻击域名解析服务器
(DNS)或 伪造域名解析服务器
(DNS)的方法,把目标网站域名解析到错误的 IP 地址从而实现用户无法访问目标网站的目的或者蓄意或恶意要求用户访问指定 IP 地址(网站)的目的。
- 通常来说存在三种情况:
路由器
被入侵DNS 服务器
被入侵- 运营商流量劫持
4. TCP 和 UDP 区别是什么?
TCP 是一个面向连接的(需要三次握手)、可靠的、基于字节流的传输层协议。而UDP 是一个面向无连接(无需建立连接)的传输层协议。(就这么简单,其它 TCP 的特性也就没有了)。
和
UDP
相比,TCP
有三大核心特性:面向连接。所谓的连接,指的是客户端和服务器的连接,在双方互相通信之前,
TCP
需要三次握手建立连接,而UDP
没有相应建立连接的过程。可靠性。
TCP
花了非常多的功夫保证连接的可靠,这个可靠性体现在哪些方面呢?一个是有状态
,另一个是可控制
。TCP 会精准记录哪些数据发送了,哪些数据被对方接收了,哪些没有被接收到,而且保证数据包按序到达,不允许半点差错。这是 有状态。
当意识到
丢包了
或者网络环境不佳
,TCP 会根据具体情况调整自己的行为,控制自己的发送速度
或者重发
。这是 可控制。相应的,UDP 就是
无状态
,不可控的
。- 为什么可靠?
顺序编号
(tcp 在传输文件的时候,会将文件拆分为多个 tcp 数据包,会将这些数据包顺序编号)确认机制
(当数据包成功的被发送方发送给接收方,接收方会根据 tcp 协议反馈给发送方一个成功接收的 ACK 信号,信号中包含了当前包的序号)超时重传
(当发送方发送数据包给接收方时,会为每一个数据包设置一个定时器,当在设定的时间内,发送方仍没有收到接收方的 ACK 信号,会再次发送该数据包,直到收到接收方的 ACK 信号或者连接已断开)校验信息
(tcp 首部校验信息较多,udp 首部校验信息较少)
- 为什么可靠?
面向字节流。
UDP
的数据传输是基于数据包
的,这是因为仅仅只是继承了 IP 层的特性,而TCP
为了维护状态,将一个个IP
包变成了 字节流。tcp
连接过程中出现的延时增加了被攻击的可能,安全性不高,而 udp 不需要连接,安全性较高tcp
是可靠的,保证数据传输的正确性,不易丢包,udp
是不可靠的,易丢包tcp
传输速率较慢,实时性差,udp
传输速率较快tcp
是字节流
模式,udp
是数据包
模式
UDP
的特点:无连接
: 不需要握手和挥手就可以直接发送数据不可靠性
- 为什么不可靠?
- 传输数据之前不需要先建立连接
- 不保证消息交付,远程主机的传输层在接收到 UDP 报文后,不需要确认
- 不保证将会顺序,不设置包序号、不重排、不会发生队首阻塞
- 不进行拥塞控制,没有内置反馈机制,不重传、无超时
- 为什么不可靠?
支持广播
首部开销小
: (tcp 建立连接需要耗时,并且 tcp 首部信息太多,每次传输的有用信息较少,实时性差)是面向报文的
无拥塞控制
tcp/udp
的使用场合?- 对数据可靠性的要求。
tcp
适用于可靠性高的场合,udp
适用于可靠性低的场合 - 应用的实时性。
tcp
有延时较大,udp
延时较小 - 网络的可靠性。网络不好的情况下使用
tcp
,网络条件好的情况下,使用udp
UDP
适用于实时应用
,例如:视频会议、直播等。TCP
适用于要求可靠性传输
的应用,例如:文件传输等。- 对数据可靠性的要求。
5. 如何理解 TCP/IP 五层模型 OSI 七层模型?
TCP/IP 五层模型(应、传、网、数、物)
- 应用层:最高层,提供特定于应用程序的协议,运行在该层的协议有 HTTP、FTP、SSH、WebSocket 等
- 传输层:为两个主机进程通信提供通用的数据传输协议,如 TCP、UDP
- 网络层:负责寻址和路由功能,将数据包发送到特定的计算机,主要协议是 IP 协议,路由器就是在这一层
- 数据链路层:负责将二进制数据包和网络信号相互转换,交换机、网卡就是在这一层(PPP 协议)
- 物理层:主要有接收器、发送器、中继器、光纤电缆等
OSI 七层模型(应、表、会、传、网、数、物)
- 应用层
- 表示层
- 会话层
- 传输层
- 网络层
- 数据链路层
- 物理层
6. 为什么 HTTPS 比 HTTP 安全?HTTPS 是如何保证安全的?
HTTPS 在 HTTP 的基础上增加了加密处理
、认证机制
和完整性保护
,我们可以将 HTTPS = HTTP + 加密 + 认证 + 完整性保护
加密
因为
HTTP
使用明文传输,中间会经过多个物理节点,可能会被劫持窃听,针对这一问题,HTTPS
采用了加密的方式解决对称加密
就是拥有一个密钥,在内容被进行加密后,需要用同一个密钥对加密内容进行解密,才能看到原本的内容。可以看作我们日常生活中的钥匙。注意:密钥在浏览器和服务器之间传输时,可能被
截取
,因此引入非对称加密
非对称加密
非对称加密有两把密钥,通常一把叫做公钥,另外一把叫做私钥。用公钥加密的内容必须用私钥才能解开,同样的,私钥加密的内容需要用公钥才能解开。
公钥加密私钥解密(数据加密) 也可用私钥加密公钥解密(签名)
改良版非对称加密
通过一组公钥、私钥已经能保证单个方向传输的安全性,那用两组公钥私钥是不是就能保证双向传输都安全了(HTTPS 的加密却没有使用这种方案)。注意:原因是非对称加密算法
非常耗时
,特别是加密解密一些较大数据的时候有些力不从心。相反,对称加密就要快很多。混合加密(HTTPS 采用这种方式加密)
思路:我们在传递过程把我们的
对称加密
中的密钥
用非对称加密
的方式去传递“
对称加密
+非对称加密
”结合的形式来实现对 HTTP 的加密- 客户端生成
会话秘钥
就是我们对称加密生成的密钥
- 它用
公钥加密
之后进行传递(这个时候被加密的不是数据 是这个会话秘钥 等于把钥匙加密了) 这里的公钥
就是非对称加密中的公钥
他是由服务器传递过去的(对外公开
) - 服务端用
非对称加密
的私钥
去解密
拿到我们的会话秘钥
- 客户端和服务端都能用同一个会话秘钥进行加解密了
- 客户端生成
就算传输过程被攻击者截取到了被加密的会话秘钥 他没有服务器的私钥是无法得到会话秘钥的。上一步我们已经解决了
数据加密
的问题 虽然攻击者无法解密数据
但是他可以篡改数据 我们怎么知道数据没被动过呢?认证
- 中间人攻击
- 某网站拥有用于
非对称加密
的公钥 A
、私钥 A
; - 浏览器向网站服务器发起请求,服务器把
公钥 A
明文给传输浏览器; 中间人劫持到公钥 A,保存下来,把数据包中的公钥 A 替换成自己伪造的公钥 B(它当然也拥有公钥 B 对应的私钥 B’);
- 浏览器随机生成一个用于
对称加密
的密钥 X
,用公钥 B
(浏览器不知道公钥被替换了)加密后传给服务器; 中间人劫持后用私钥 B’ 解密得到密钥 X,再用公钥 A 将 X 加密后传给服务器;
- 服务器拿到后用
私钥 A
解密得到密钥 X
。
- 某网站拥有用于
在双方都不会发生异常的情况下,中间人得到了
密钥 X
,这其中的根本原因就是浏览器无法确认自己收到的公钥是不是网站的, 如何解决这一问题呢? - 数字证书数字证书(解决
身份伪装
问题)如何证明浏览器收到的公钥一定是该网站的公钥?
这里就需要有一个
公信机构
给网站颁发一个“身份证”了。网站在使用 HTTPS 前,需要向“CA 机构”
申请颁发一份数字证书
,数字证书里有证书持有者
、证书持有者的公钥
等信息,服务器把证书传输给浏览器,浏览器从证书里取公钥就行了,证书就如同身份证一样,可以证明“该公钥对应该网站”。然而到这里还是有一个问题,
如何保证证书在传输的过程不会被篡改
,身份证本身有防伪的技术,那么如何保证证书的防伪呢?数字签名(解决
数据篡改
问题)如何保证证书不被篡改?
我们把证书内容生成一份“签名”,比对
证书内容
和签名
是否一致就能察觉是否被修改,这种技术就称为: 数字签名数字签名的制作过程?
- CA 拥有
非对称加密
的私钥
和公钥
; - CA 对证书明文信息进行
Hash
; - 对
Hash
后的值用私钥
加密,得到数字签名 S
;
将明文和数字签名共同组成数字证书,这样一份证书就可以颁发给网站了。
- CA 拥有
浏览器得到证书后如何验证这份证书的真实性?
- 拿到服务器发送过来的证书,得到
明文 T
,数字签名 S
; - 用
CA 机构
的公钥
对S 解密
(由于是浏览器信任的机构,所以浏览器保有 CA 的公钥),得到S
‘; - 浏览器用证书说明的
Hash 算法
对明文 T
进行Hash
得到T
; - 比较
S
是否等于T
,等于则代表证书可信。
- 拿到服务器发送过来的证书,得到
浏览器如何得到权威机构的公钥?
上面提到,如何要对服务器发过来的证书进行解密,那么就需要到 CA 的公钥,因为其被 CA 的私钥给加密了。那么浏览器是如何拥有 CA 的公钥呢?
实际上权威机构的公钥并不需要传输,因为权威机构会和主流的浏览器或操作系统合作,将他们的公钥内置在浏览器或操作系统环境中。客户端收到证书之后,只需要从证书中找到权威机构的信息,并从本地环境中找到权威机构的公钥,就能正确解密 CA 公钥。
中间人有可能篡改证书吗?(不可以)
- 中间人攻击
HTTPS 的请求流程
- 客户端向服务器发起 HTTPS 请求,连接到服务器的 443 端口
- 服务器端有一个密钥对,即
公钥
和私钥
,是用来进行非对称加密使用的,服务器端保存着私钥
,不能将其泄露,公钥
可以发送给任何人 - 服务器将自己的
公钥
包含在权威机构发布的证书
中发送给客户端 - 客户端收到服务器端的
证书
之后,会对证书进行检查,验证其合法性,如果发现发现证书有问题,那么 HTTPS 传输就无法继续。严格的说,这里应该是验证服务器发送的数字证书
的合法性,关于客户端如何验证数字证书的合法性。如果公钥合格,那么客户端会生成一个随机值,这个随机值就是用于进行对称加密
的密钥
,我们将该密钥称之为client key
,即客户端密钥,这样在概念上和服务器端的密钥容易进行区分。然后用服务器的公钥对客户端密钥进行非对称加密,这样客户端密钥就变成密文了,至此,HTTPS 中的第一次 HTTP 请求结束; - 客户端会发起 HTTPS 中的第二个 HTTP 请求,将被公钥所加密之后的客户端密钥发送给服务器
- 服务器接收到客户端发来的密文之后,会用自己的私钥对其进行非对称解密,解密之后的明文就是
客户端密钥
,然后用客户端密钥对数据进行对称加密,这样数据就变成了密文 - 然后服务器用对称加密的密钥(即客户端密钥)对报文进行加密,并将加密后的报文发送给客户端
- 客户端收到服务器发送来的密文,用客户端密钥对其进行对称解密,得到服务器发送的数据。这样 HTTPS 中的第二个 HTTP 请求结束,整个 HTTPS 传输完成
7. HTTP 常见的状态码有哪些?适用的场景?
- 1.xx: 信息类
- 2.xx: 成功类
- 【200 OK】:请求成功
- 【204 No Content】: 与 200 基本相同,但响应头没有 body 数据
- 【206 Partial Content】: 应用于 HTTP
分块下载
或断点续传
。表示响应返回的 body 数据并不是资源的全部,而是其中的一分部
- 3.xx: 重定向
- 【301 Moved Permanently】:永久重定向,说明请求资源不存在了,需改用新的 URL 再次访问
- 【302 Found】:临时重定向,说明请求资源还在,但暂时需要用另一个 URL 来访问
- 4.xx: 客户端错误
- 【400 Bad Request】:表示客户端请求报文有错误
- 【403 Forbidden】: 表示服务器禁止访问资源,并不是客户端请求出错
- 【404 Not Found】:表示请求资源在服务器上不存在或未找到
- 5.xx: 服务端错误
- 【500 Internal Server Error】:与 400 类似,是个笼统的错误码,表示服务器发生了错误
- 【501 No Implement】: 表示客户端请求的功能还不支持
- 【502 Bad Gatwy】:通常服务器作为网关或代理时返回的错误码
- 【503 Service Unavailable】: 表示服务器当前很忙,暂时无法响应
8. 知道 HTTP 的缓存吗?(浏览器强缓存和协商缓存)
强缓存: 是利用 Expires
(http1.0
)和 Cache-Control
(http1.1
)这两个字段来控制的,控制资源缓存的时间,在有效期内不会去向服务器请求了,同时存在,优先 Cache-Control
Expires
的值为一个绝对时间
(由于Expires
是依赖于客户端系统时间,当修改了本地时间后,缓存可能会失效)- 给
Cache-Control
设置max-age
,表示缓存的最长时间是多少秒,是一个相对时间
。 Catch-Control
的值:public
都缓存;private
客服端缓存,服务器不缓存;no-cache
:表示不进行强缓存,发送HTTP
请求,用协商缓存来验证;no-store
所有内容都不缓存;s-maxage
:这和max-age
长得比较像,但是区别: 在于s-maxage
是针对代理服务器
的缓存时间
协商缓存: 是由服务器来确定缓存资源是否可用,是利用 Last-Modified
(http1.0
)(表示被请求资源在服务器端的最后一次修改时间)/ If-Modified-Since
和 ETag
(每次文件修改后服务端那边会生成一个新的 ETag
)/if-None-Match
来控制的,同时存在,优先 ETag
Last-Modified/if-Modified-Since
的缺点:(文件有可能在 1s 内修改内容、文件内容修改后又复原)ETag
性能上的不足, 只要文件发生改变,ETag
就会发生改变.ETag
需要服务器通过算法来计算出一个 hash 值缓存位置
Service Worker
Service Worker
借鉴了Web Worker
的 思路,即让JS 运行
在主线程
之外,由于它脱离了浏览器的窗体,因此无法直接访问 DOM。虽然如此,但它仍然能帮助我们完成很多有用的功能,比如离线缓存
、消息推送
和网络代理
等功能。其中的离线缓存就是Service Worker Cache
。Memory Cache
Disk Cache
Push Cache
(HTTP2)
浏览器的本地存储主要分为 Cookie
、WebStorage
和 IndexedDB
, 其中 WebStorage
又可以分为 localStorage
和 sessionStorage
。
9. 前端错误的分类有哪些?
- 前端错误类型:
SyntaxError
(语法错误)ReferenceError
(引用错误)TypeError
(类型错误)RangeError
(范围越界错误)
- 前端错误分类:
- 代码执行的错误
- 资源加载的错误
- 前端错误捕获:
- try...catch
能捕获
同步运行时
错误。不能捕获语法错误
、异步任务错误
、promise
、资源加载错误
等 - window.onerror 能捕获所有同步错误、普通的异步错误。不能捕获语法、async 任务、promise 错误和资源加载错误等(例如:iframe)
- window.addEventListener('error', function() {}) 可捕获资源加载错误(如果资源加载错误,说明跨域,添加 crossorigin)
- window.addEventListener('unhandledrejection', function() {})
捕获 promise 错误(没有写
catch
的Promise
中抛出的错误无法被onerror
或try-catch
捕获到。为了防止有漏掉的 Promise 异常,建议在全局增加一个对unhandledrejection
的监听,用来全局监听Uncaught Promise Error
) - Vue 项目异常:Vue.config.errorHandler = (err, vm, info) => {}
- 崩溃和卡顿:利用
window
对象的load
和beforeunload
事件
- try...catch
能捕获
- 总结:
- 可疑区域使用:
Try...Catch
- 全局监控 JS 异常使用:
window.onerror
- 全局监控静态资源异常使用:
window.addEventListener
- 捕获没有 catch 的 Promise 异常使用:
unhandledrejection
Vue errorHandler
和React componentDidCatch
- 监控网页崩溃和卡顿使用:
window
对象的loader
和beforeunload
- 跨域:添加
crossOrigin
- 可疑区域使用:
- 上报方式有哪些:
XMLHttpRequest
有跨域限制,携带 cookie 问题;上报请求可能会阻塞业务;请求容易丢失Image
可跨域、get 请求,大小限制- SendBean
10. 说说为什么前端会有跨域?如何解决跨域?知道 option 请求吗?
不同域之间相互请求资源,称为”跨域“;浏览器的 同源策略(同源:url
是由协议、域名、端口和路径等组成。如果两个路径的协议、域名、端口都相同则表示再同一个域上)
在浏览器上 script
、img
、link
、iframe
等标签都可以加载跨域资源 且不受同源限制
非同源限制:
- 无法读取非同源网页的 Cookie、LocalStorage、IndexedDB
- 无法接触非同源网页的 DOM
- 无法向非同源地址发起 ajax 请求
解决跨域的方式:
- 设置 document.domain 解决无法读取非同源网页的 Cookie 问题
浏览器是通过
document.domain
属性来检查两个页面是否同源,因此只要通过设置相同的document.domain
,两个页面就可以共享 Cookie。缺点:此方案仅限主域相同,子域不同的跨域应用场景
- JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本 IE),
缺点:是只支持
get 请求
, 需要后台配合,将返回结果包装成callback(res)
的形式。原理是利用script
元素的跨域能力核心思想:网页通过添加一个
script
元素,向服务器请求JSON
数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来原生 js 实现
【请求地址后面添加?callback="xxx",然后在 script 中添加 xxx 方法其参数返回结果】jQuery 的 ajax
【dataType: 'jsonp', 请求方式为 jsonp;jsonpCallback: 'handleCallback' 自定义回调函数】扩展:
script
的src
和img
的src
跨域的区别?
原理上都是利用标签的
src
可绕过同源限制,跨域请求的特点;区别在于:img 只能单向发送 get 请求,不可访问响应内容(只是展现),而 script 可对其进行解析
- 如果黑客植入
script
脚本通过jsonp
的方式对服务器进行攻击,怎么办?
可以通过页面设置的
内容安全协议 csp
进行防范- 为什么
jsonp
只支持get
请求?
JSONP
是一种【请求一段JS 脚本
,把执行这段脚本的结果当做数据】的玩法。JSONP
的实现原理就是document.createElement('script')
生成一个script 标签
,然后插body
里而已。在这里根本没有设置请求格式
的余地). 所以jsonp
不会对服务器端代码或者内容做更改
H5 提供的 postMessage
postMessage
事件发送消息,message
事件接受消息跨域资源共享 CORS【常用】
浏览器将
CORS
请求分成两类:简单请求
和非简单请求
;当发出简单请求,只需要在头信息之中增加一个Origin 字段
。当发出CORS
非简单请求,会在正式通信之前,增加一次OPTIONS
查询请求,称为"预检"请求(preflight
)。设置响应头的
Access-Control-Allow-Origin: *
- 扩展:简单请求同时满足的三个条件?
- 请求方式只能是:
GET
、POST
、HEAD
HTTP
请求头限制这几种字段:Accept
、Accept-Language
、Content-Language
、Content-Type
、Last-Event-ID
Content-type
只能取:application/x-www-form-urlencoded
(是 Jquery 的 Ajax 请求默认方式)、multipart/form-data
、text/plain
- 请求方式只能是:
预检请求(
preflight
):浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP
动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest
请求,否则就报错。重点:服务端如何避免每次都发出预检请求?(
缓存
)Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。在有效期间,不用发出另一条预检请求(全局和局部方式)【常用】@CrossOrigin
注解,可细粒度精确到单个请求级别
- 扩展:简单请求同时满足的三个条件?
window.name
name
值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的name
值(2MB)vue
中proxy
配置本地代理扩展:说说
Webpack Proxy
工作原理?proxy
工作原理实质上是利用 http-proxy-middleware 这个http
代理中间件,实现请求转发给其他服务器H5 中的 websocket
Nginx 代理
扩展:跨域请求如何携带 Cookie?
- 前端请求时在
request
对象中配置:withCredentials: true
- 服务端在
response
的header
中配置:Access-Control-Allow-Origin: "http://xxx.com"
- 服务器在
response
的header
中配置:Access-Control-Allow-Credentials: true
11. TCP 三次握手 & 四次挥手(短连接 & 长连接)?
三次握手的主要作用,需要确认双方的两样能力: 发送的能力
和接收的能力
从最开始双方都处于 CLOSED
状态。然后服务端开始监听某个端口,进入了 LISTEN
状态。
- 客户端给服务端发一个
SYN
报文,并指明客户端的初始化序列号seq
。此时客户端处于SYN_SEND
状态。 - 服务端接收到,返回
SYN
和ACK
(对应客户端发来的 SYN),确认号ack
,自己变成了SYN-REVD
- 之后客户端再发送 确认应答
ACK
报文 给服务端,自己变成了ESTABLISHED
状态;服务端收到ACK
之后,也变成了ESTABLISHED
状态。
为什么不是两次?
根本原因:
无法确认客户端的接收能力。
- 第一次握手:客户端发送网络包,服务端收到了。服务端就能得出结论:
客户端的发送能力
、服务端的接收能力
是正常的 - 第二次握手:服务端发包,客户端收到了。客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。注意:此时服务器并不能确认客户端的
接收能力是否正常
- 第三次握手:客户端发包,服务端收到了。服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
- 第一次握手:客户端发送网络包,服务端收到了。服务端就能得出结论:
为什么不是四次?
三次握手的目的: 是确认双方
发送
和接收
的能力,四次也可以。没必要什么是半连接队列?
三次握手前,服务端的状态从
CLOSED
变为LISTEN
, 同时在内部创建了两个队列:半连接队列
和全连接队列
,即SYN 队列
和ACCEPT 队列
。半连接队列: 当客户端发送
SYN
到服务端,服务端收到以后回复ACK
和SYN
,状态由LISTEN
变为SYN_RCVD
,此时这个连接就被推入了SYN 队列
,也就是半连接队列
。全连接队列: 就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现
丢包
现象。补充一点 关于
SYN-ACK
重传次数的问题:服务器发送完
SYN-ACK 包
,如果未收到客户确认包,服务器进行首次重传
,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。三次握手过程中可以携带数据么?
第三次握手的时候,可以携带。前两次握手不能携带数据
。(假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的
SYN 报文
中放入大量的数据。这会让服务器花费很多时间、内存空间来接收这些报文。原因就是会让服务器更加容易受到攻击了)SYN 攻击
是什么?SYN 攻击: 就是
Client
在短时间内伪造大量不存在的IP 地址
,并向Server
不断地发送SYN 包
,Server
则回复确认包,并等待Client
确认,由于源地址不存在,因此Server
需要不断重发直至超时,这些伪造的SYN 包
将长时间占用未连接队列
,导致正常的SYN
请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击
是一种典型的DoS/DDoS 攻击
。在
Linux/Unix
上可以使用系统自带的netstats
命令来检测SYN 攻击
。netstat -n -p TCP | grep SYN_RECV
- 常见的防御
SYN 攻击
的方法有如下几种:- 缩短超时(
SYN Timeout
)时间 - 增加最大半连接数
- 过滤网关防护
SYN cookies
技术
- 缩短超时(
- 常见的防御
TCP
的半关闭(half-close
)所谓的半关闭,其实就是
TCP
提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。
刚开始双方处于 ESTABLISHED
状态
客户端要断开了,向服务器发送
FIN 报文
,发送后客户端变成了FIN-WAIT-1
状态。 注意, 这时候客户端同时也变成了half-close
(半关闭)状态,即无法向服务端发送报文,只能接收。- 关闭
TCP 连接
的挥手,客户端和服务端都可以发起关闭操作,以客户端发起为例:- 浏览器先发送
FIN 报文
、Seq
初始化序列号给服务器,并停止发送数据,但仍可以接受服务端响应的数据 - 服务器收到后,发送
ACK=浏览器序列号+1
给浏览器,表明收到 - 服务器数据都发完了,给浏览器发送
FIN 报文
、Seq
序列号给浏览器 - 浏览器收到后,发送
ACK=服务器序列号+1
给服务器,表明收到
- 浏览器先发送
- 关闭
为什么是
四次挥手
而不是三次
?因为服务端在接收到
FIN
, 往往不会立即返回FIN
, 必须等到服务端所有的报文都发送完毕了,才能发FIN
。因此先发一个ACK
表示已经收到客户端的FIN
,延迟一段时间才发FIN
。这就造成了四次挥手
。- 如果是
三次挥手
会有什么问题?
(等于说服务端将
ACK
和FIN
的发送合并为一次挥手,这个时候长时间的延迟
可能会导致客户端误以为FIN
没有到达客户端,从而让客户端不断的重发FIN
。)- 如果是
等待
2MSL
的意义(为什么要等一段时间再关闭,不等不行吗?)这是为了防止发送给服务器的确认报文段
丢失
或者出错
,从而导致服务端不能正常关闭。等待时间是2MSL
,这也是报文在网络上的最大生存时间,超过这个时间就会被丢弃。RFC793
中规定MSL
为 2 分钟,如果超过这个时间,那么主动关闭者就会发送一个RST
状态位的包,表示重置连接
,这时候被关闭者就知道对方已经关闭了连接。
12. 知道第三方 Cookie 吗?
cookie
由哪些部分组成:Name
: 这个属性就表示cookie
的名字,每个cookie
的名字都是唯一的Value
: 这个属性表示cookie
的值Domain
: 就是cookie
所在的域名,如果没有设置domain
的话,那么cookie
会自动绑定到执行语句的当前域Path
: 这个属性的默认值是/,匹配的是路由,这里匹配的是路由的意思就是比如你的域名是www.xxx.xyz
,那么路由如果是www.xxx.xyz/auth/
,那么实际上cookie
绑定的是这个/auth
Max-age
: 这个属性是http1.1
新增的属性,用来替代expires
的,单位是秒,用来表示cookie
在多少秒之后会失效Secure
: 因为http
是无状态协议,而且http
在传输数据的过程中是以明文传输的,因此很容易遭到第三方网站的窃取,如果我们使用secure
的话,就能够确保cookie
是在https
协议下进行传输的,但是这不代表会将cookie
加密HttpOnly
: 只能通过HTTP
协议传输,不能通过JS 访问
。这个属性表示不能够被js 脚本
访问,因为js
能够通过document.cookie
来获取cookie
,所以使用HttpOnly
就能够阻止这种情况,在一定程度上防止xss 攻击
,也就是跨站脚本攻击
SameSite
: 用于限制第三方网站的cookie
发送机制(cookie
每次随着请求会自动发送到服务器去的,这就给了其他站点发起CSRF 攻击
和用户追踪的机会) 具体如下:Strict
: 最严格的模式,完全禁止跨站点请求时携带cookie
,设置为strict
之后,跨站行为都不会再携带cookie
Lax
: 相对strict 模式
会宽松一点儿,允许导航到三方网站时携带cookie
,即a 标签跳转
,form 表单的 get 提交
,以及link 标签的 prerender
None
: 使用None
显示的关闭SameSite
模式控制,但是需要注意的是还需要加上secure
,即cookie
只会在HTTPS
中发送,如果只是设置了SameSite=None
是没有效果的
Cookie
的分类:第一方 Cookie 和 第三方 Cookie,这两类
Cookie
都是网站保存在用户电脑上的一个文件,它们都由某个特定的域创建,并且只能被这个域访问第一方 Cookie
是由地址栏中列出的网站域设置的Cookie
,而第三方 Cookie
来自在网页上嵌入广告或图片等项的其他域来源。都是网站在客户端上存放的一小块数据。他们都由某个域存放,只能被这个域访问。他们的区别其实并不是技术上的区别,而是使用方式上的区别。
Cookie
的特性:- 生命周期:
Expires
(过期时间) 和Max-Age
(单位秒)设置 - 作用域:
Domain
和Path
设置。注意:/
表示域名下的任何路径都允许使用Cookie
- 安全相关
- 如果带上
Secure
,说明只能通过HTTPS
传输cookie
- 如果
cookie
字段带上HttpOnly
,说明只能通过HTTP
协议传输,不能通过JS 访问
。也是预防XSS 攻击
重要手段 SameSite
属性有Strict
、Lax
、None
(默认)。也是预防CSRF 攻击
的重要手段
- 如果带上
- 生命周期:
Cookie
的缺点:- 容量缺陷:
Cookie
的体积上限只有4KB
,只能用来存储少量的信息 - 性能缺陷:
Cookie
紧跟域名,不管域名下面的某一个地址需不需要这个Cookie
,请求都会携带上完整的Cookie
,这样随着请求数的增多,其实会造成巨大的性能浪费的,因为请求携带了很多不必要的内容。但可以通过Domain
和Path
指定作用域
来解决。 - 安全缺陷。由于
Cookie
以纯文本
的形式在浏览器和服务器中传递,很容易被非法用户截获,然后进行一系列的篡改,在Cookie
的有效期内重新发送给服务器,这是相当危险的。另外,在HttpOnly
为false
的情况下,Cookie
信息能直接通过JS 脚本
来读取。
- 容量缺陷:
13. CSRF 和 XSS 、SSRF 的攻击原理 与防御措施?
CSRF
(Cross-site request forgery
):跨站请求伪造: 是一种劫持受信任用户向服务器发送非预期请求的攻击方式
CSRF
的攻击原理
从上图可以看出,要完成一次 CSRF 攻击
,受害者必须满足 两个必要的条件
:
- 登录受信任
网站 A
,并在本地生成Cookie
。(如果用户没有登录网站 A
,那么网站 B
在诱导的时候,请求网站 A
的api 接口
时,会提示你登录) - 在不登出
A
的情况下,访问危险网站B
(其实是利用了 ∂网站 A
的漏洞)
温馨提示一下:cookie
保证了用户可以处于登录状态,但网站 B
其实拿不到 cookie
CSRF
如何防御方法一、
Token
验证:(用的最多)服务器发送给客户端一个
token
;客户端提交的表单中带着这个token
。如果这个token
不合法,那么服务器拒绝这个请求。方法二、
隐藏令牌
:把
toke
n 隐藏在http
的head
头中。方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。方法三、
Referer
验证:Referer
指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。
XSS
(Cross Site Scripting
):跨域脚本攻击: 是指攻击者在网站上注入恶意的客户端代码,通过恶意脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式
XSS
的攻击原理XSS 攻击
的核心原理是:不需要你做任何的登录认证,它会通过合法的操作(比如在url 中输入
、在评论框中输入
),向你的页面注入脚本(可能是js
、html 代码块
等)。导致:盗用Cookie
、破坏页面的正常结构,插入广告等恶意内容、D-doss 攻击XSS
的攻击方式反射型
发出请求时,
XSS 代码
出现在url
中,作为输入提交到服务器端,服务器端解析后响应,XSS 代码
随响应内容一起传回给浏览器,最后浏览器解析执行XSS
代码。这个过程像一次反射,所以叫反射型 XSS
存储型
存储型 XSS
和反射型 XSS
的差别在于,提交的代码会存储在服务器端(数据库
、内存
、文件系统
等),下次请求时目标页面时不用再提交XSS 代码
XSS
的防范措施(encode
+过滤
)方法一、
编码
对用户输入的数据进行
HTML Entity
编码;把字符转换成 转义字符方法二、
过滤
移除用户输入的和事件相关的属性。如 onerror 可以自动触发攻击,还有 onclick 等。(总而言之,过滤掉一些不安全的内容) 移除用户输入的
Style 节点
、Script 节点
、Iframe 节点
。(尤其是 Script 节点,它可是支持跨域的呀,一定要移除)方法三、
校正
避免直接对
HTML Entity
进行解码; 使用DOM Parse
转换,校正不配对的DOM
标签
CSRF
和XSS
的区别:- CSRF:需要用户先登录
网站 A
,获取cookie
; XSS:不需要登录 CSRF
:是利用网站 A
本身的漏洞,去请求网站 A
的api
;XSS:是向网站 A
注入JS 代码
,然后执行 JS
里的代码,篡改网站 A
的内容
- CSRF:需要用户先登录
SSRF
扩展:v-html
的弊端?(利用 innerHTML
)
- 可能会导致
xss 攻击
v-html
会替换掉标签内部的子元素
14. V8 中执行一段 JS 代码的整个过程
- 首先通过
词法分析
和语法分析
生成AST
- 词法分析 即
分词
,它的工作就是将一行行的代码分解成一个个token
。 - 语法分析 ,将生成的这些
token
数据,根据一定的语法规则转化为AST
。
- 词法分析 即
- 将
AST
转换为字节码
- 由
解释器
逐行执行字节码
,遇到热点代码
启动编译器
进行编译,生成对应的机器码
, 以优化执行效率 (现在不用一次性将全部的字节码都转换成机器码,而是通过解释器来逐行执行字节码,省去了生成二进制文件的操作,这样就大大降低了内存的压力。)
注意:因为机器码相比字节码的体积太大,引发了严重的内存占用问题。机器只认识机器码
JS 运行
可以分为 编译阶段 和 执行阶段
函数体内 的
编译
四步曲,发生在函数执行之前
:- 创建
AO
对象, (activation object
) - 找
形参
和变量
声明,将形参和变量声明作为AO
对象的属性名,值为undefined
- 将
实参
和形参
相统一 - 在函数体里找函数声明,将函数名作为
AO
对象的属性名,值赋予函数体
- 创建
全局下 的
编译
三步曲,发生在代码最前面
:- 创建
GO
对象 - 找变量声明,将变量声明作为
GO
对象的属性名,值赋予undefined
- 找全局里的函数声明,将函数名作为
GO
对象的属性名,值赋予函数体
- 创建
14. 从输入 URL 到页面展示经历了什么?
总体流程如下:
URL 解析 完整的 URL:
协议 + 主机 + 端口 + 路径 + 参数 + 锚点
。如果为非 url 结构的字符串,交给浏览器默认引擎去搜索改字符串;若为 url 结构的字符串,浏览器主进程会交给 网络进程 ,开始干活。- encodeURI 和 encodeURIComponent 的区别?
encodeURI 是编码
整个URL
,而 encodeURIComponent 编码的是参数
部分
- encodeURI 和 encodeURIComponent 的区别?
encodeURI 是编码
检查资源缓存 在有效期内的缓存资源直接使用,称之为
强缓存
。返回 200,size 为 memory cache(资源从内存中取出)和 disk cache(资源从磁盘中取出)。当超过有效期的,则携带缓存的资源标识向服务器发起请求。返回 304,走协商缓存
;返回 200,向服务器发起请求,将结果缓存起来,为下一次使用 通常来说:刷新页面会使用内存缓存
; 关闭后重新打开会使用磁盘缓存
DNS 解析:将域名解析成 IP 地址 如果没有成功使用本地缓存,则需要发起网络请求,发起之前要做 DNS 解析,会依次搜索:
浏览器DNS缓存 -> 操作系统DNS缓存 -> 路由器DNS缓存 -> 服务商DNS服务器查询 -> 全球13台根域名服务器查询
解析域名的查找过程:浏览器的 DNS 缓存 -> 操作系统的 DNS 缓存 -> 本地域名服务器(递归查询自己的 DNS 缓存) -> 上级域名服务器(进行迭代查询)【本地域名服务器 -> 根域名服务器(会返回顶级域名的服务器地址); 本地域名服务器 -> 顶级域名服务器(返回权威域名服务器地址,全球 13 台) 】 -》解析到的 IP 地址返回给操作系统,并缓存起来。-》操作系统将 IP 地址返回给浏览器,并缓存起来,供下次使用。
为了节约时间,可以在 HTML 头部做 DNS 的预解析:
<!-- 在HTTPS下开启 --> <meta http-equiv="x-dns-prefetch-control" content="on" /> <link rel="dns-prefetch" href="http://www.baidu.com" />
为了保证响应的及时,DNS 解析使用的是 UDP 协议。浏览器根据自定义的规则,提前去解析后面可能用到的域名,来加速网站的访问速度。
注意:a 标签的 href 是可以在 chrome。firefox 包括高版本的 IE,但是在 HTTPS 下面不起作用,需要 meta 来强制开启功能。
TCP 连接:TCP 三次握手
三次握手过程:
- 客户端发送一个带 SYN=1, Seq=X 的数据包到服务器(第一次握手,由浏览器发起,告诉服务器,我要发送请求了)
- 服务器返回一个带 SYN=1, Ack=X+1, Seq=Y 的响应包确认信息给浏览器(第二次握手,由服务器发起,告诉浏览器我准备好了,你发吧)
- 客户端回传一个带 ACK=Y+1, Seq=Z 的数据包,代表”握手结束“(第三次握手,由浏览器发起,告诉浏览器,我马上就发了,准备接受吧)
扩展:为啥要三次握手?目的:为了防止已失效的连接请求报文突然又传送到服务器,因而产生错误。
发送 HTTP 请求
服务器处理请求并返回 HTTP 报文
浏览器解析并渲染页面
断开 TCP 连接:TCP 四次挥手
- 四次挥手过程:
- 主动方发送一个 Fin=1 结束标志位,Ack=Z 组成的报文,并发送 Seq=X 序列号。并进入到 FIN_WAIT_1 状态(第一次挥手:由浏览器发起的,发送给服务器,我请求报文发送完了,你准备关闭吧)
- 被动方发送 ACK=X+1 确认标志位报文,并发送 Seq=Z 的序列号。表示同意关闭请求。此时主机发起方进入 FIN_WAIT_2 阶段。(第二次挥手:由服务器发起的,告诉浏览器,我请求报文接受完了,我准备关闭了,你也准备吧)
- 被动方发送 Fin=1 结束标志位,Ack=X 组成的报文,并发送 Seq=Y 的序列号。进入 LAST_ACK 状态(第三次挥手:由服务器发起,告诉浏览器,我响应报文发送完了,你准备关闭吧)
- 发送方发送 ACK=Y 确认标志位报文,并发送 Seq=Z 的序列号。进入等待 TIME_WAIT 状态。被动方收到发起方的报文段以后关闭连接。发起方等待一定时间未收到回复,则正常关闭。(第四次挥手:由浏览器发起,告诉服务器,我响应报文接受完了,我准备关闭了,你也准备吧)
- 四次挥手过程:
15. 说说浏览器渲染原理?
整个渲染流程,从 HTML
到 DOM
、样式计算
、布局
、图层
、绘制
、光栅化
、合成
和显示
解析
HTML
,生成DOM
树。解析CSS
,生成CSSOM
树浏览器从磁盘或网络读取
HTML
的原始字节,并根据文件的指定编码(例如UTF-8
)将它们转换成字符串。DOM 树构建步骤:
字节数据
=》字符串
=》Token
=>Node
(节点对象) =》DOM
生成节点对象并构建
DOM
,每个Token
被生成后,会立刻消耗这个Token
创建出节点对象。注意:带有结束标签标识的 Token 不会创建节点对象。关于
CSS 样式
,它的来源一般是三种:link 标签
引用style 标签
中的样式- 元素的
内嵌 style 属性
格式化样式表:首先,浏览器是无法直接识别 CSS 样式文本的,因此渲染引擎接收到
CSS 文本
之后第一件事情就是将其转化为一个结构化的对象,即styleSheets
标准化样式属性:有一些
CSS 样式
的数值并不容易被渲染引擎所理解,因此需要在计算样式之前将它们标准化,如em->px
,red->#ff0000
,bold->700
等等。计算每个节点的具体样式:样式已经被
格式化
和标准化
, 接下来就可以计算每个节点的具体样式信息了。计算的方式也并不复杂,主要就是两个规则: 继承和层叠。- 每个子节点都会默认
继承父节点
的样式属性,如果父节点中没有找到,就会采用浏览器默认样式
,也叫UserAgent 样式
。这就是 继承规则 - 然后是
层叠规则
,CSS
最大的特点在于它的层叠性,也就是最终的样式取决于各个属性共同作用的效果 注意:在计算完样式之后,所有的样式值会被挂在到window.getComputedStyle
当中,也就是可以通过JS
来获取计算后的样式
- 每个子节点都会默认
将
DOM 树
和CSS 树
结合,生成渲染/布局树
(Render Tree
) Chrome 团队已经做了大量的重构,已经没有生成Render Tree
的过程了。而布局树的信息已经非常完善,完全拥有Render Tree
的功能。布局树
生成的大致工作如下:- 遍历生成的
DOM 树
节点,并把他们添加到布局树中。 - 计算布局树节点的坐标位置。
- 遍历生成的
Layout
(回流):根据生成的渲染树,进行回流
(Layout),得到节点的几何信息(位置、大小)Painting
(重绘):根据渲染树以及回流得到节点的几何信息,从而得到节点的绝对像素Display
:将像素发送给GPU
。展示在页面上
注意:渲染树只包含可见的节点
扩展:为了构建渲染树,浏览器主要完成了以下工作:
从
DOM 树
的根节点开始遍历每个可见节点
对于每个可见的节点,找到
CSSOM 树
中对于的规则,并应用它们根据每个可见节点以及其对应的样式,组合生成渲染树
- 不可见的节点包括:
- 一些不会渲染输出的节点:如:
script
、link
、meta
等 - 一些通过
CSS
进行隐藏的节点:如:display: none
- 一些不会渲染输出的节点:如:
- 不可见的节点包括:
注意:利用 visible
和 opacity
隐藏节点,还是会显示在渲染树上的
分层:渲染引擎给页面分了很多图层,这些图层按照一定顺序叠加在一起,就形成了最终的页面。完成图层树的构建后,渲染引擎会对图层树中的每个图层进行绘制,为图层绘制。然后进行栅格化(raster)操作(绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的),最后合成与显示。
扩展:CSS 加载会阻塞页面显示吗?
- css 加载不会阻塞 DOM 树的解析
- css 加载会阻塞 DOM 树的渲染
- css 加载会阻塞后面 js 语句的执行
so, 为了防止 css 阻塞,引起页面白屏,可以提高页面加载速度:
- 使用 cdn
- 对 css 进行压缩
- 合理利用缓存
- 减少 http 请求,将多个 css 文件合并
16. 前端需要注意哪些 SEO?
- 合理的
title
、description
、keywords
- 语义化
HTML 代码
- 重要内容
HTML 代码
放在前面(搜索引擎抓取HTML
顺序是从上到下),不要用js 输出
(爬虫不会执行 js) - 少用
iframe
(搜索引擎不会抓取 `iframe 中的内容) - 非装饰性图片必须加
alt
- 提高网站速度(网站速度是搜索引擎排序的一个重要指标)
17. 什么是堆?什么是栈?它们之间有什么区别和联系?
堆和栈的概念存在于数据结构
中和操作系统内存
中。
- 在数据结构中,栈中数据的存取方式为
先进后出
。而堆是一个优先队列
,是按优先级来进行排序的,优先级可以按照大小来规定。完全 二叉树是堆的一种实现方式。 - 在操作系统中,内存被分为
栈区
和堆区
。栈区内存由编译器自动分 配释放
,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。堆区内存一般由程序员分配释放
,若程序员不释放,程序结束时可能由垃圾回收机制回收
。