笔者开源的前端进阶之道已有三年之久,至今也有 17k star,承蒙各位读者垂爱。在当下部份内容已经略微过期,所以决定提笔翻新内容。html
翻新后的内容会所有集合在「干爆前端」中,有兴趣的读者能够前往查看。前端
阅读前重要提示:git
本文非百科全书,只专为面试复习准备、查漏补缺、深刻某知识点的引子、了解相关面试题等准备。github
笔者一直都是崇尚学会面试题底下涉及到的知识点,而不是刷一大堆面试题,结果变了个题型就不会的那种。因此本文和别的面经不同,旨在提炼面试题底下的经常使用知识点,而不是甩一大堆面试题给各位看官。web
你们也能够在笔者的网站上阅读,体验更佳!面试
姊妹篇:17K star 仓库,解决 90% 的大厂基础面试题算法
今天的文章从输入 URL 开始,和你们聊聊这其中前端工程师须要掌握的网络相关的内容。浏览器
注:输入 URL 到返回数据过程当中会涉及到不少网络相关知识,笔者只会介绍前端工程师必须了解的那部分,其余的内容读者能够自行学习。缓存
当咱们在浏览器中键入 URL:www.google.com/ 时,浏览器会先去寻找该域名所对应的 IP 地址,毕竟最终通讯咱们仍是得用 IP 才能找到对方的地址,域名只是方便用户记忆的别名。安全
那么如何找到域名所对应的 IP 地址呢?接下来让笔者先来给你们介绍咱们遇到的第一块内容: DNS。
DNS 的做用就是经过域名查询到具体的 IP。
由于 IP 存在数字和英文的组合(IPv6),很不利于人类记忆,因此就出现了域名。你能够把域名当作是某个 IP 的别名,DNS 就是去查询这个别名的真正名称是什么。
当你在浏览器中想访问 www.google.com
时,会经过进行如下操做:
com
这个一级域名的服务器google.com
这个二级域名www.google.com
这个三级域名的地址 以上介绍的是 DNS 递归查询,还有种是迭代查询,区别就是前者是由系统配置的 DNS 服务器作请求,获得结果后将数据返回给客户端;后者由客户端去作请求。
这时可能会有好奇的读者会问:DNS 在作解析的时候,向这些服务器发送的请求究竟是基于 TCP 仍是 UDP 协议的?
其实在当前的网络环境中,这两种协议都有用的。经过 UDP 去进行一些数据量少的请求,这时候能用到 UDP 性能快的优点;对于数据量大且须要保证数据完整有序的时候会选择用 TCP 去请求,保证数据的正确及完整性。
后续的内容中咱们也会介绍 UDP 及 TCP 协议。
当浏览器获取域名的 IP 地址后,立刻会经过 TCP 协议与服务器创建链接。接下来咱们来聊聊 TCP 协议的内容。
TCP 创建链接须要进行三次握手:
最开始两端都为 CLOSED 状态。在通讯开始前,双方都会建立 TCB(一个数据结构,里面包含了协议须要的不少内容,有兴趣的能够自行了解)。 服务器建立完 TCB 后遍进入 LISTEN 状态,此时开始等待客户端发送数据。
第一次握手
客户端向服务端发送链接请求报文段(SYN)。该报文段中包含自身的数据通信初始序号。请求发送后,客户端便进入 SYN-SENT 状态,x
表示客户端的数据通讯初始序号。
第二次握手
服务端收到链接请求报文段后,若是赞成链接,则会发送一个应答(ACK + SYN),该应答中也会包含自身的数据通信初始序号,发送完成后便进入 SYN-RECEIVED 状态。
第三次握手
当客户端收到链接赞成的应答后,还要向服务端发送一个确认报文(ACK)。客户端发完这个报文段后便进入ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED 状态,此时链接创建成功。
PS:第三次握手能够包含数据,经过 TCP 快速打开(TFO)技术。其实只要涉及到握手的协议,均可以使用相似 TFO 的方式,客户端和服务端存储相同 cookie,下次握手时发出 cookie 达到减小 RTT(一次请求来回的时间) 的目的。
这里会有个经典面试题:为何 TCP 须要三次而不是两次握手?
由于这是为了防止失效的链接请求报文段被服务端接收,从而产生错误。
能够想象以下场景。客户端发送了一个链接请求 A,可是由于网络缘由形成了超时,这时 TCP 会启动超时重传的机制再次发送一个链接请求 B。此时请求顺利到达服务端,服务端应答完就创建了请求。若是链接请求 A 在两端关闭后终于抵达了服务端,那么这时服务端会认为客户端又须要创建 TCP 链接,从而应答了该请求并进入 ESTABLISHED 状态。此时客户端实际上是 CLOSED 状态,那么就会致使服务端一直等待,形成资源的浪费。
PS:在创建链接中,任意一端掉线,TCP 都会重发 SYN 包,通常会重试五次,在创建链接中可能会遇到 SYN FLOOD 攻击。遇到这种状况你能够选择调低重试次数或者干脆在不能处理的状况下拒绝请求。
当创建链接并请求完成后,TCP 链接并不会立刻断开。得益于 HTTP 1.1 协议中的 keep-alive 属性,链接能够短暂的保留一段时间,具体如何断开得看服务端的设置或者客户端主动 close 掉。
一旦 close,TCP 协议就会进行四次握手来断开链接。
第一次握手
若客户端 A 认为数据发送完成,则它须要向服务端 B 发送链接释放请求。
第二次握手
B 收到链接释放请求后,会告诉应用层要释放 TCP 连接。而后会发送 ACK 包,并进入 CLOSE_WAIT 状态,表示 A 到 B 的链接已经释放,不接收 A 发的数据了。可是由于 TCP 链接时双向的,因此 B 仍旧能够发送数据给 A。
第三次握手
B 若是此时还有没发完的数据会继续发送,完毕后会向 A 发送链接释放请求,而后 B 便进入 LAST-ACK 状态。
PS:经过延迟确认的技术(一般有时间限制,不然对方会误认为须要重传),能够将服务端的第二次和第三次握手合并,延迟 ACK 包的发送。
第四次握手
A 收到释放请求后,向 B 发送确认应答,此时 A 进入 TIME-WAIT 状态。该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,若该时间段内没有 B 的重发请求的话,就进入 CLOSED 状态。当 B 收到确认应答后,也便进入 CLOSED 状态。
为何 A 要进入 TIME-WAIT 状态,等待 2MSL 时间后才进入 CLOSED 状态?
为了保证 B 能收到 A 的确认应答。若 A 发完确认应答后直接进入 CLOSED 状态,若是确认应答由于网络问题一直没有到达,那么会形成 B 不能正常关闭。
创建和断开链接涉及到的两种握手都是 TCP 中很重要的知识。固然除了这些内容以外,TCP 也还有不少东西咱们须要学习,好比说 TCP 协议是如何实现有序且完整的传递数据,这其中涉及到的内容咱们立刻就会学到。
首先咱们先来学习 TCP 协议是如何实现完整送达数据的。毕竟网络波动及各类不肯定因素的存在会致使数据传递发生丢失,一旦出现这种状况咱们得确保协议有重传的机制。就比如咱们在业务中上传某些重要数据同样,一次没收到响应或者发生别的状况时,会进行屡次重试。
ARQ 协议也就是超时重传机制协议。经过确认和超时机制保证了数据的正确送达,其中包含中止等待 ARQ 和连续 ARQ 协议。
正常传输过程
只要 A 向 B 发送一段报文,都要中止发送并启动一个定时器,等待对端回应,在定时器时间内接收到对端应答就取消定时器并发送下一段报文。
出现错误时
1 .报文丢失或出错
在报文传输的过程当中可能会出现丢包。这时候超过定时器设定的时间就会再次发送丢包的数据直到对端响应,因此须要每次都备份发送的数据。
即便报文正常的传输到对端,也可能出如今传输过程当中报文出错的问题。这时候对端会抛弃该报文并等待 A 端重传。
PS:通常定时器设定的时间都会大于一个 RTT 的平均时间。
2. ACK 超时或丢失
对端传输的应答也可能出现丢失或超时的状况。那么超过定时器时间 A 端照样会重传报文。这时候 B 端收到相同序号的报文会丢弃该报文并重传应答,直到 A 端发送下一个序号的报文。
在超时的状况下也可能出现应答很迟到达,这时 A 端会判断该序号是否已经接收过,若是接收过只须要丢弃应答便可。
这个协议的缺点就是传输效率低,在良好的网络环境下每次发送报文都得等待对端的 ACK 。
在连续 ARQ 中,发送端拥有一个发送窗口,能够在没有收到应答的状况下持续发送窗口内的数据,这样相比中止等待 ARQ 协议来讲减小了等待时间,提升了效率。
累计确认
连续 ARQ 中,接收端会持续不断收到报文。若是和中止等待 ARQ 中接收一个报文就发送一个应答同样,就太浪费资源了。经过累计确认,能够在收到多个报文之后统一回复一个应答报文。报文中的 ACK 能够用来告诉发送端这个序号以前的数据已经所有接收到了,下次请发送这个序号 + 1的数据。
可是累计确认也有一个弊端。在连续接收报文时,可能会遇到接收到序号 5 的报文后,并未接到序号 6 的报文,然而序号 7 之后的报文已经接收。遇到这种状况时,ACK 只能回复 6,这样会形成发送端重复发送数据,这种状况下能够经过 Sack 来解决,这个会在下文说到。
窗口这个概念在 TCP 中常常会看到,就好比咱们在上面小节中讲到了发送窗口。在 TCP 中,两端都维护着窗口:分别为发送端窗口和接收端窗口。
发送端窗口包含已发送但未收到应答的数据和能够发送可是未发送的数据。
发送端窗口大小是由接收窗口剩余大小决定的。接收方会把当前接收窗口的剩余大小写入应答报文,发送端收到应答后根据该值和当前网络拥塞状况设置发送窗口的大小,因此发送窗口的大小是不断变化的。
当发送端接收到应答报文后,会随之将窗口进行滑动
刷过算法的同窗看到这两张图应该会很熟悉,毕竟滑动窗口在算法中也是一类高频题目。
滑动窗口实现了流量控制。接收方经过报文告知发送方还能够发送多少数据,从而保证接收方可以来得及接收数据。
在发送报文的过程当中,可能会遇到对端出现零窗口的状况。在该状况下,发送端会中止发送数据,并启动 persistent timer 。该定时器会定时发送请求给对端,让对端告知窗口大小。在重试次数超过必定次数后,可能会中断 TCP 连接。
拥塞处理是 TCP 中做用很大的功能模块,主要经过一些算法来控制数据的传输,防止拥塞网络。
拥塞处理和流量控制不一样,后者是做用于接收方,保证接收方来得及接受数据。而前者是做用于网络,防止过多的数据拥塞网络,避免出现网络负载过大的状况。
拥塞处理包括了四个算法,分别为:慢开始,拥塞避免,快速重传,快速恢复。
慢开始算法,顾名思义,就是在传输开始时将发送窗口慢慢指数级扩大,从而避免一开始就传输大量数据致使网络拥塞。举个例子在平常下载时,咱们的下载网速都是逐渐变快的。
慢开始算法步骤具体以下:
拥塞避免算法相比简单点,每过一个 RTT 窗口大小只加一,这样可以避免指数级增加致使网络拥塞,慢慢将大小调整到最佳值。
在传输过程当中若是协议认为网络拥塞了,会立刻进行如下步骤:
快速重传通常和快恢复一块儿出现。一旦接收端收到的报文出现失序的状况,接收端只会回复最后一个顺序正确的报文序号(没有 Sack 的状况下)。若是收到三个重复的 ACK,说明发送端传过去的数据对端都没有收到,此时会启动快速重传。主要算法为:
TCP Reno
TCP New Reno 算法改进了以前 TCP Reno 算法的缺陷。在以前,快恢复中只要收到一个新的 ACK 包,就会退出快恢复。
在 TCP New Reno 中,TCP 发送方先记下三个重复 ACK 的分段的最大序号。
假如我有一个分段数据是 1 ~ 10 这十个序号的报文,其中丢失了序号为 3 和 7 的报文,那么该分段的最大序号就是 10。发送端只会收到 ACK 序号为 3 的应答。这时候重发序号为 3 的报文,接收方顺利接收并会发送 ACK 序号为 7 的应答。这时候 TCP 知道对端是有多个包未收到,会继续发送序号为 7 的报文,接收方顺利接收并会发送 ACK 序号为 11 的应答,这时发送端认为这个分段接收端已经顺利接收,接下来会退出快恢复阶段。
说完了 TCP 协议,咱们再来聊聊它的兄弟协议 UDP 吧。由于 UDP 相对 TCP 功能简单,所以涉及到的知识并很少。
UDP 是一个面向报文(报文能够理解为一段段的数据)的协议。意思就是 UDP 只是报文的搬运工,不会对报文进行任何拆分和拼接操做。TCP 协议就会这样干,毕竟得保证报文的有序。
具体来讲:
因为 UDP 对于报文处理的很简单,所以会带来一些弊端。
由于 UDP 没有 TCP 那么复杂,须要保证数据不丢失且有序到达。因此 UDP 的头部开销小,只有八字节,相比 TCP 的至少二十字节要少得多,在传输数据报文时是很高效的。
头部包含了如下几个数据
讲完 TCP 及 UDP 这两块内容之后,咱们接下去看浏览器对于 www.google.com/ 连接在进行完 TCP 三次握手后的后续动做是什么。
由于咱们输入的协议是 HTTPS,这个协议咱们能够当作是 HTTP 协议加上 TLS 安全协议。所以在进行完 TCP 链接后不会立刻开始 HTTP 协议层面的传输,而是开始 TLS 协议的握手。
HTTPS 最重要的组成部分就是 TLS 协议了,由于是这个协议保证了安全性。
既然须要保证安全性,那么确定须要用到加密技术。在 TLS 中使用了两种加密技术,分别为:对称加密和非对称加密。
对称加密:
对称加密就是两边拥有相同的秘钥,两边都知道如何将密文加密解密。
非对称加密:
有公钥私钥之分,公钥全部人均可以知道,能够将数据用公钥加密,可是将数据解密必须使用私钥解密,私钥只有分发公钥的一方才知道。
TLS 握手过程以下图:
经过以上步骤可知,在 TLS 握手阶段,两端使用非对称加密的方式来通讯,可是由于非对称加密损耗的性能比对称加密大,因此在正式传输数据时,两端使用对称加密的方式通讯。
PS:以上说明的都是 TLS 1.2 协议的握手状况,在 1.3 协议中,首次创建链接只须要一个 RTT,后面恢复链接不须要 RTT 了。所以若是在乎网络传输性能的话,应该选用 TLS1.3 协议。
当 TLS 完成握手之后,就真的开始进行 HTTP 协议层面的传输数据了。
对于 HTTP 协议来讲,前端工程师主要了解常见状态码以及 header 便可,毕竟这些是咱们平常编码中常常须要接触的内容。
2XX 成功
3XX 重定向
4XX 客户端错误
5XX 服务器错误
通用字段 | 做用 |
---|---|
Cache-Control | 控制缓存的行为 |
Connection | 浏览器想要优先使用的链接类型,好比 keep-alive 、close |
Date | 建立报文时间 |
Pragma | 报文指令 |
Via | 代理服务器相关信息 |
Transfer-Encoding | 传输编码方式 |
Upgrade | 要求客户端升级协议 |
Warning | 在内容中可能存在错误 |
请求字段 | 做用 |
---|---|
Accept | 能正确接收的媒体类型 |
Accept-Charset | 能正确接收的字符集 |
Accept-Encoding | 能正确接收的编码格式列表 |
Accept-Language | 能正确接收的语言列表 |
Expect | 期待服务端的指定行为 |
From | 请求方邮箱地址 |
Host | 服务器的域名 |
If-Match | 两端资源标记比较 |
If-Modified-Since | 本地资源未修改返回 304(比较时间) |
If-None-Match | 本地资源未修改返回 304(比较标记) |
User-Agent | 客户端信息 |
Max-Forwards | 限制可被代理及网关转发的次数 |
Proxy-Authorization | 向代理服务器发送验证信息 |
Range | 请求某个内容的一部分 |
Referer | 表示浏览器所访问的前一个页面 |
TE | 传输编码方式 |
响应字段 | 做用 |
---|---|
Accept-Ranges | 是否支持某些种类的范围 |
Age | 资源在代理缓存中存在的时间 |
ETag | 资源标识 |
Location | 客户端重定向到某个 URL |
Proxy-Authenticate | 向代理服务器发送验证信息 |
Server | 服务器名字 |
WWW-Authenticate | 获取资源须要的验证信息 |
实体字段 | 做用 |
---|---|
Allow | 资源的正确请求方式 |
Content-Encoding | 内容的编码格式 |
Content-Language | 内容使用的语言 |
Content-Length | request body 长度 |
Content-Location | 返回数据的备用地址 |
Content-MD5 | Base64加密格式的内容 MD5检验值 |
Content-Range | 内容的位置范围 |
Content-Type | 内容的媒体类型 |
Expires | 内容的过时时间 |
Last_modified | 内容的最后修改时间 |
最后几段内容咱们再来聊聊一些新的协议,先来聊聊 HTTP 2.0。这个协议相比于 HTTP 1.1 而言,能够说是大幅度提升了 web 的性能。
在 HTTP 1.1 中,为了性能考虑,咱们会引入雪碧图、将小图内联、使用域名发散等等的方式。这一切都是由于浏览器限制了同一个域名下的请求数量,当页面中须要请求不少资源的时候,队头阻塞(Head of line blocking)会致使在达到最大请求数量时,剩余的资源须要等待其余资源请求完成后才能发起请求。
可是在 HTTP 2.0 中这个问题被极大地优化了,可是仍是没有被解决。由于 HTTP 2.0 底下仍是 TCP 协议,TCP 须要保证数据正确性的作法也会带来队头阻塞的问题,因此说问题并没被解决。可是这个问题被后续的新协议完全解决了,咱们下文再表。
咱们先来感觉下 HTTP 2.0 比 HTTP 1.X 到底快了多少,能够经过 该连接 体验。
在 HTTP 1.X 中,由于队头阻塞的缘由,你会发现请求是这样的
在 HTTP 2.0 中,由于引入了多路复用,你会发现请求是这样的:
在 HTTP 2.0 中,有两个很是重要的概念,分别是帧(frame)和流(stream)。
帧表明着最小的数据单位,每一个帧会标识出该帧属于哪一个流,流也就是多个帧组成的数据流。
多路复用,就是在一个 TCP 链接中能够存在多条流。换句话说,也就是能够发送多个请求,对端能够经过帧中的标识知道属于哪一个请求。经过这个技术能够极大的提升传输性能。
HTTP 2.0 中全部增强性能的核心点在于此。在以前的 HTTP 版本中,咱们是经过文本的方式传输数据。在 HTTP 2.0 中引入了新的编码机制,全部传输的数据都会被分割,并采用二进制格式编码为二进制帧。
二进制帧分为不少类型,在上图中咱们能够发现存在了 HEADERS 帧和 DATA 帧,除了这些以外还有还几种,各位读者有兴趣的话能够自行了解。
在 HTTP 1.X 中,咱们使用文本的形式传输 header,在 header 携带 cookie 的状况下,可能每次都须要重复传输几百到几千的字节。
在 HTTP 2.0 中,使用了 HPACK 压缩格式对传输的 header 进行编码,减小了 header 的大小。并在两端维护了索引表,用于记录出现过的 header ,后面在传输过程当中就能够传输已经记录过的 header 的键名,对端收到数据后就能够经过键名找到对应的值。
这个不用学了,由于用的太少,Chrome 或移除这个功能了。详情见 Chrome to remove HTTP/2 Push。
这是一个谷歌出品的基于 UDP 实现的同为传输层的协议,目标很远大,但愿替代 TCP 协议。
网络协议涉及到的内容其实还有至关多,笔者这里聊了聊前端工程师必需要学习的内容。若是你们对某一个协议感兴趣的话能够自行学习。
举几个例子:
抛一些学习引子给各位读者。对于上文中的内容若是你们有疑惑的话能够一块儿交流学习。
你们也能够在笔者的网站上阅读,体验更佳!