# 网络篇

# 浏览器从输入网址到页面展示的过程

浏览器从输入网址到渲染页面主要分为以下几个过程

  • URL 输入
  • DNS 解析
  • 建立 TCP 连接
  • 发送 HTTP / HTTPS 请求(建立 TLS 连接)
  • 服务器响应请求
  • 浏览器解析渲染页面
  • HTTP 请求结束,断开 TCP 连接

yanyue404 - #223 从输入 URL 到页面加载的过程(上) (opens new window)

yanyue404 - #227 从输入 URL 到页面加载的过程(下) (opens new window)

# GET 和 POST 的区别有哪些?

从这几个方面,应用场景、缓存、参数类型、安全方面、长度方面

  • 应用场景:GET 请求是一个幂等的请求,一般 Get 请求用于对服务器资源不会产生影响的场景,比如说请求一个网页的资源。而 Post 不是一个幂等的请求,一般用于对服务器资源会产生影响的情景,比如注册用户这一类的操作。
  • 缓存:浏览器一般会对 Get 请求缓存,但很少对 Post 请求缓存。
  • 发送的报文格式:Get 请求的报文中实体部分为空,Post 请求的报文中实体部分一般为向服务器发送的数据。
  • 安全性:Get 请求可以将请求的参数放入 url 中向服务器发送,这样的做法相对于 Post 请求来说是不太安全的,因为请求的 url 会被保留在历史记录中。
  • 请求长度:浏览器由于对 url 长度的限制,所以会影响 get 请求发送数据时的长度。这个限制是浏览器规定的,并不是 RFC 规定的。
  • 参数类型:post 的参数传递支持更多的数据类型。

post 请求的编码格式

  1. application/x-www-form-urlencoded (get 请求默认这个, 浏览器的原生 form 表单)
  2. multipart/form-data (该种方式也是一个常见的 POST 提交方式,通常表单上传文件时使用该种方式)
  3. application/json (服务器消息主体是序列化后的 JSON 字符串。)

# POST 和 PUT 请求的区别

  • PUT 请求是向服务器端发送数据,从而修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次 PUT 操作,其结果并没有不同。(可以理解为时更新数据)
  • POST 请求是向服务器端发送数据,该请求会改变数据的种类等资源,它会创建新的内容。(可以理解为是创建数据)

# TCP 三次握手和四次挥手是什么?

三次握手

三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送 3 个包。

  1. 第一次握手([SYN], Seq = x)

客户端发送一个 SYN 标记的包,Seq 初始序列号 x,发送完成后客户端进入SYN_SEND状态。

  1. 第二次握手([SYN,ACK], Seq = y, ACK = x + 1)

服务器返回确认包(ACK)应答,同时还要发送一个 SYN 包回去。ACK = x + 1,表示确认收到(客户端发来的 Seq 值 + 1),Seq = y, 表示让客户端确认是否能收到。发送完成后服务端进入SYN_RCVD(received 缩写,收到)状态。

  1. 第三次握手([ACK], ACK = y + 1)

客户端再次发送确认包(ACK),ACK = y + 1, 表示确认收到服务器的包(服务端发来的 Seq 值 + 1)。客户端发送完毕后,进入ESTABLISHED(建立了)状态,服务端接收到这个包,也进入ESTABLISHED状态, TCP 握手结束。

换一种抽象派的方式解释:

(1)客户端:hello,你是 server 么?

(2)服务端:hello,我是 server,你是 client 么?

(3)客户端:yes,我是 client

四次挥手

建立连接成功后,接下来就正式传输数据

然后,待到断开连接时,需要进行四次挥手(因为是全双工的,所以需要四次挥手)

TCP 连接的断开需要发送四个包,所以称为四次挥手。

  1. 第一次挥手([FIN], Seq = x)

客户端发送一个 FIN 标记的包,告诉服务器需要关闭连接,表示自己不用发送数据了,但是还可以接收数据。发送完成后,客户端进入 FIN_WAIT_1 状态。

  1. 第二次挥手 ([ACK], ACK = x + 1)

服务端发送一个 ACK 的确认包,告诉客户端接收到关闭的请求,但是还没有准备好。发送完成后,服务端进入 CLOSE_WAIT 状态,客户端收到这个包后,进入 FIN_WAIT_2,等待服务器关闭连接。

  1. 第三次挥手 ([FIN], Seq = y)

服务端准备好关闭连接时,发送 FIN 标记的包,告诉客户端准备关闭了。发送完成后,服务端进入 LAST_ACK 状态,等待客户端确认。

  1. 第四次挥手 ([ACK], ACK = y + 1)

客户端接收到服务端的关闭请求,再发送 ACK 标记的确认包,进入 TIME_WAIT 状态,等待服务端可能请求重传的 ACK 包。

服务端接收到 ACK 包后,关闭连接,进入 CLOSED 状态。

客户端在等待固定时间(两个最大段生命周期)后,没有接收到服务的 ACK 包,认为服务器已关闭连接,自己也关闭连接,进入 CLOSED 状态。

同样换一种抽象派的方式解释:

(1)主动方:我已经关闭了向你那边的主动通道了,只能被动接收了

(2)被动方:收到通道关闭的信息

(3)被动方:那我也告诉你,我这边向你的主动通道也关闭了

(3)主动方:最后收到数据,之后双方无法通信

为什么要"三次握手,四次挥手"

  1. 三次握手

换个易于理解的视角来看为什么要 3 次握手。

客户端和服务端通信前要进行连接,“3 次握手”的作用就是双方都能明确自己和对方的收、发能力是正常的

第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。 从客户端的视角来看,我接到了服务端发送过来的响应数据包,说明服务端接收到了我在第一次握手时发送的网络包,并且成功发送了响应数据包,这就说明,服务端的接收、发送能力正常。而另一方面,我收到了服务端的响应数据包,说明我第一次发送的网络包成功到达服务端,这样,我自己的发送和接收能力也是正常的。

第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力,服务端的发送、接收能力是正常的。 第一、二次握手后,服务端并不知道客户端的接收能力以及自己的发送能力是否正常。而在第三次握手时,服务端收到了客户端对第二次握手作的回应。从服务端的角度,我在第二次握手时的响应数据发送出去了,客户端接收到了。所以,我的发送能力是正常的。而客户端的接收能力也是正常的。

经历了上面的三次握手过程,客户端和服务端都确认了自己的接收、发送能力是正常的。之后就可以正常通信了。

每次都是接收到数据包的一方可以得到一些结论,发送的一方其实没有任何头绪。我虽然有发包的动作,但是我怎么知道我有没有发出去,而对方有没有接收到呢?

而从上面的过程可以看到,最少是需要三次握手过程的。两次达不到让双方都得出自己、对方的接收、发送能力都正常的结论。其实每次收到网络包的一方至少是可以得到:对方的发送、我方的接收是正常的。而每一步都是有关联的,下一次的“响应”是由于第一次的“请求”触发,因此每次握手其实是可以得到额外的结论的。比如第三次握手时,服务端收到数据包,表明看服务端只能得到客户端的发送能力、服务端的接收能力是正常的,但是结合第二次,说明服务端在第二次发送的响应包,客户端接收到了,并且作出了响应,从而得到额外的结论:客户端的接收、服务端的发送是正常的。

  1. 四次挥手

TCP 连接是双向传输的对等的模式,就是说双方都可以同时向对方发送或接收数据。当有一方要关闭连接时,会发送指令告知对方,我要关闭连接了。这时对方会回一个 ACK,此时一个方向的连接关闭。但是另一个方向仍然可以继续传输数据,等到发送完了所有的数据后,会发送一个 FIN 段来关闭此方向上的连接。接收方发送 ACK 确认关闭连接。注意,接收到 FIN 报文的一方只能回复一个 ACK, 它是无法马上返回对方一个 FIN 报文段的,因为结束数据传输的“指令”是上层应用层给出的,我只是一个“搬运工”,我无法了解“上层的意志”。

参考链接

  • https://zhuanlan.zhihu.com/p/53374516
  • https://www.jianshu.com/p/12790cea57ac

# 强缓存与协商缓存

HTTP 缓存可以简单的划分成两种类型:强缓存200 from cache)与协商缓存304

区别简述如下:

  • 强缓存可以通过 Expires / Cache-Control 控制,命中强缓存时不会发起网络请求,资源直接从本地获取,浏览器显示状态码 200 from cache。
  • 协商缓存可以通过 Last-Modified / If-Modified-Since 和 Etag / If-None-Match 控制,开启协商缓存时向服务器发送的请求会带上缓存标识,若命中协商缓存服务器返回 304 Not Modified 表示浏览器可以使用本地缓存文件,否则返回 200 OK 正常返回数据。(如下图)

协商缓存依赖于服务端与浏览器之间的通信,对于协商缓存,使用Ctrl + F5 强制刷新可以使得缓存无效(因为会刷新变成首次请求服务器,不会带有协商缓存的标识)

但是对于强缓存,在未过期时,必须更新资源路径才能发起新的请求(更改了路径相当于是另一个资源了,这也是前端工程化中常用到的技巧)

# HTTP 状态码 304 是多好还是少好

服务器为了提高网站访问速度,对之前访问的部分页面指定缓存机制,当客户端在此对这些页面进行请求,服务器会根据缓存内容判断页面与之前是否相同,若相同便直接返回 304,此时客户端调用缓存内容,不必进行二次下载。

状态码 304 不应该认为是一种错误,而是对客户端有缓存情况下服务端的一种响应。

搜索引擎蜘蛛会更加青睐内容源更新频繁的网站。通过特定时间内对网站抓取返回的状态码来调节对该网站的抓取频次。若网站在一定时间内一直处于 304 的状态,那么蜘蛛可能会降低对网站的抓取次数。相反,若网站变化的频率非常之快,每次抓取都能获取新内容,那么日积月累,的回访率也会提高。

产生较多 304 状态码的原因:

  • 页面更新周期长或不更新
  • 纯静态页面或强制生成静态 html

304 状态码出现过多会造成以下问题:

  • 网站快照停止;
  • 收录减少;
  • 权重下降。

# HTTP 缓存有哪些方案?

http1.0 中的缓存控制:

  1. Expires

过去我们一直使用 Expires 来实现强缓存:当服务器返回响应时,在 Response Headers 中将过期时间写入 Expires 字段(服务器端时间)。例如:

从上图我们可以看到 Expires 是一个时间戳,接下来如果我们试图再次向服务器请求资源,浏览器就会先对比本地时间和 expires 的时间戳,如果本地时间小于 expires 设定的过期时间,就直接从缓存中获取这个资源。

到这里聪明的你可能已经发现了下面这个问题:由于 expires 的时间戳是服务器定义的,而本地时间的取值来自客户端,因此 expires 的工作机制对于客户端时间和服务器时间的一致性要求极高,如果两者的时间存在时差,会带来意料之外的结果。

由于上面的原因,加上 expires 是 http1.0 的产物,现在实现强缓存大多数是使用 http 1.1 的 Cache-Control

  1. If-Modified-Since/Last-Modified

Last-Modified(Response Header)和 If-Modified-Since(Request Header)是一对报文头,属于 HTTP1.0。

Last-Modified 表示资源的最后修改时间,是一个时间戳,如果启用了协商缓存,它会在客户端首次请求时随着 Response Headers 返回。

Last-Modified: Sat, 09 May 2020 09:33:56 GMT

If-Modified-Since 是一个请求首部字段,并且只能用在 GET 或 HEAD 请求中。客户端再次请求服务器时,请求头会包含这个字段,后面跟着在缓存中获取的资源的最后修改时间。

If-Modified-Since: Sat, 09 May 2020 09:33:56 GMT

服务端收到请求发现此请求头中有 If-Modified-Since 字段,会与被请求资源的最后修改时间进行对比,如果一致则会返回 304 和响应报文头,浏览器从缓存中获取数据即可。从字面上看,就是说从某个时间节点开始看,是否被修改了,如果被修改了,就返回整个数据和 200 OK,如果没有被修改,服务端只要返回响应头报文,304 Not Modified,Response Headers 不会再添加 Last-Modified 字段。

使用 Last-Modified 是有一定缺陷的:

  • 如果资源更新的速度是秒以下单位,那么该缓存是不能被使用的,因为 If-Modified-Since 只能检查到以秒为最小计量单位的时间差。
  • 如果文件是通过服务器动态生成的,那么该方法的更新时间永远是生成的时间,尽管文件可能没有变化,所以起不到缓存的作用。
  • 我们编辑了文件,但文件的内容没有改变。服务端并不清楚我们是否真正改变了文件,它仍然通过最后编辑时间进行判断。因此这个资源在再次被请求时,会被当做新资源,进而引发一次完整的响应——不该重新请求的时候,也会重新请求。

为了解决上面服务器没有正确感知文件变化的问题,http 1.1 中的 Etag 作为 Last-Modified 的补充出现了。

http1.1 中的缓存控制:

  1. Cache-Control

Cache-Control 是 HTTP1.1 提出的特性,为了弥补Expires缺陷提出的,提供了更精确细致的缓存功能。

Cache-Control 包含的值很多:

  • public:表明响应可以被任何对象(包括:发送请求的客户端、代理服务器等等)缓存。
  • private:表明响应只能被客户端缓存。
  • no-cache:跳过强缓存,直接进入协商缓存阶段。
  • no-store:表示当前请求资源禁用缓存
  • max-age=:设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)
  • s-maxage=:覆盖 max-age 或者 Expires 头。如果 s-maxage 未过期,则向代理服务器请求其缓存内容。

这里需要注意的是:s-maxage 仅在代理服务器中生效,客户端只需要考虑 max-age。

下面我们来看个例子:

从上面的例子我们可以看到,HTTP 响应报文中同时有 Cache-ControlExpires两个字段,由于Cache-Control优先级较高,那么直接根据 Cache-Control 的值进行缓存,也就是说在 315360000 秒内重新发起该请求,会直接使用缓存结果,强制缓存生效。

在 HTTP1.1 标准试图将缓存相关配置收敛进 Cache-Control 这样的大背景下, max-age 可以视作是对 expires 能力的补位/替换。在当下的前端实践里,我们普遍会倾向于使用 max-age。但如果你的应用对向下兼容有强诉求,那么 expires 仍然是不可缺少的。

  1. If-None-Match/E-tag

Etag 和 If-None-Match 是一对报文头,属于 HTTP1.1。

Etag 是一个响应首部字段,是根据实体内容生成的一段 hash 字符串,标识资源的状态,由服务端产生。

ETag: W/"324023994867772d0dd9fac01f1420bd"

If-None-Match 是一个条件式的请求首部,如果客户端请求资源时在请求首部加上这个字段,值为之前服务器返回的 Etag,则当且仅当服务器上没有任务资源的 Etag 属性值与这个值相符,服务器才会返回带有请求资源实体的 200 响应,否正服务器会返回不带实体的 304 响应。

If-None-Match: W/"324023994867772d0dd9fac01f1420bd"

Etag 的生成过程需要服务器额外付出开销,会影响服务端的性能,这是它的弊端。因此启用 Etag 需要我们审时度势。正如我们刚刚所提到的:Etag 并不能替代 Last-Modified,它只能作为 Last-Modified 的补充和强化存在。 Etag 在感知文件变化上比 Last-Modified 更加准确,优先级也更高。当 Etag 和 Last-Modified 同时存在时,以 Etag 为准。

Max-Age VS Expires

Expires使用的是服务器端的时间

但是有时候会有这样一种情况-客户端时间和服务端不同步

那这样,可能就会出问题了,造成了浏览器本地的缓存无用或者一直无法过期

所以一般 http1.1 后不推荐使用Expires

Max-Age使用的是客户端本地时间的计算,因此不会有这个问题

因此推荐使用Max-Age

注意,如果同时启用了Cache-ControlExpiresCache-Control优先级高。

E-tag VS Last-Modified

Last-Modified

  • 表明服务端的文件最后何时改变的
  • 它有一个缺陷就是只能精确到 1s,
  • 然后还有一个问题就是有的服务端的文件会周期性的改变,导致缓存失效

E-tag

  • 是一种指纹机制,代表文件相关指纹
  • 只有文件变才会变,也只要文件变就会变,
  • 也没有精确时间的限制,只要文件一变,立马 E-tag 就不一样了

注意,如果同时带有E-tagLast-Modified,服务端会优先检查E-tag

完整的缓存规则

但是如何设置一个可靠的缓存规则,需要根据实际需求决定,绝大部分需求的缓存规则都可以根据 Chrome 官方提供的流程图来进行设置。

我们一起来看上面这张流程图:

  • 如果资源不可复用,直接为 Cache-Control 设置 no-store,拒绝一切形式的缓存;
  • 如果资源可复用,考虑是否每次都需要向服务器进行缓存确认,如果是,设置 Cache-Control 的值为 no-cache;
  • 如果不需要每次都向服务器确认,考虑资源是否可以被代理服务器缓存,根据其结果决定是设置为 private 还是 public;
  • 接下来考虑资源的过期时间,设置对应的 max-age;
  • 最后,配置协商缓存需要用到的 Etag、Last-Modified 等参数。

后续根据这个流程,我们就可以设计出可靠的缓存规则了。

最佳实践

资源尽可能命中强缓存,且在资源文件更新时保证用户使用到最新的资源文件。

  • 强缓存只会命中相同命名的资源文件。
  • 在资源文件上加 hash 标识(webpack 可在打包时在文件名上带上)。
  • 通过更新资源文件名来强制更新命中强缓存的资源。

# 什么是 HTTPS 协议?

超文本传输安全协议(Hypertext Transfer Protocol Secure,简称:HTTPS)是一种通过计算机网络进行安全通信的传输协议。HTTPS 经由 HTTP 进行通信,利用 SSL/TLS 来加密数据包。HTTPS 的主要目的是提供对网站服务器的身份认证,保护交换数据的隐私与完整性。

HTTP 协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,而协议 TLS/SSL 具有身份验证信息加密完整性校验的功能,可以避免此类问题发生。

安全层的主要职责就是对发起的 HTTP 请求的数据进行加密操作对接收到的 HTTP 的内容进行解密操作

TLS/SSL 的工作原理

TLS/SSL 全称安全传输层协议(Transport Layer Security), 是介于 TCP 和 HTTP 之间的一层安全协议,不影响原有的 TCP 协议和 HTTP 协议,所以使用 HTTPS 基本上不需要对 HTTP 页面进行太多的改造。

TLS/SSL 的功能实现主要依赖三类基本算法:散列函数 hash、对称加密、非对称加密。这三类算法的作用如下:

  • 基于散列函数验证信息的完整性
  • 对称加密算法采用协商的秘钥对数据加密
  • 非对称加密实现身份认证和秘钥协商

TLS/SSL 的工作方式就是客户端使用非对称加密与服务器进行通信,实现身份的验证并协商对称加密使用的秘钥。对称加密算法采用协商秘钥对信息以及信息摘要进行加密通信,不同节点之间采用的对称秘钥不同,从而保证信息只能通信双方获取。这样就解决了两个方法各自存在的问题。

# HTTPS 是如何保证安全的?

先理解两个概念:

  • 对称加密:即通信的双⽅都使⽤同⼀个秘钥进⾏加解密,对称加密虽然很简单性能也好,但是⽆法解决⾸次把秘钥发给对⽅的问题,很容易被⿊客拦截秘钥。
  • ⾮对称加密:
  1. 私钥 + 公钥= 密钥对

  2. 即⽤私钥加密的数据,只有对应的公钥才能解密,⽤公钥加密的数据,只有对应的私钥才能解密

  3. 因为通信双⽅的⼿⾥都有⼀套⾃⼰的密钥对,通信之前双⽅会先把⾃⼰的公钥都先发给对⽅

  4. 然后对⽅再拿着这个公钥来加密数据响应给对⽅,等到到了对⽅那⾥,对⽅再⽤⾃⼰的私钥进⾏解密

⾮对称加密虽然安全性更⾼,但是带来的问题就是速度很慢,影响性能。

AES 对称加密

RSA 非对称加密

解决⽅案:

结合两种加密⽅式,将对称加密的密钥使⽤⾮对称加密的公钥进⾏加密,然后发送出去,接收⽅使⽤私钥进⾏解密得到对称加密的密钥,然后双⽅可以使⽤对称加密来进⾏沟通。

此时⼜带来⼀个问题,中间⼈问题:

如果此时在客户端和服务器之间存在⼀个中间⼈,这个中间⼈只需要把原本双⽅通信互发的公钥,换成⾃⼰的公钥,这样中间⼈就可以轻松解密通信双⽅所发送的所有数据。

所以这个时候需要⼀个安全的第三⽅颁发证书(CA),证明身份的身份,防⽌被中间⼈攻击。 证书中包括:签发者、证书⽤途、使⽤者公钥、使⽤者私钥、使⽤者的 HASH 算法、证书到期时间等。

但是问题来了,如果中间⼈篡改了证书,那么身份证明是不是就⽆效了?这个证明就⽩买了,这个时候需要⼀个新的技术,数字签名。

数字签名就是⽤ CA ⾃带的 HASH 算法对证书的内容进⾏ HASH 得到⼀个摘要,再⽤ CA 的私钥加密,最终组成数字签名。当别⼈把他的证书发过来的时候,我再⽤同样的 Hash 算法,再次⽣成消息摘要,然后⽤ CA 的公钥对数字签名解密,得到 CA 创建的消息摘要,两者⼀⽐,就知道中间有没有被⼈篡改了。这个时候就能最⼤程度保证通信的安全了。

# HTTP/1.1 和 HTTP/2 的区别有哪些?

  1. HTTP/2 使用了二进制传输,而且将 head 和 body 分成帧来传输;HTTP/1.1 是字符串传输。
  2. HTTP/2 支持多路复用,HTTP/1.1 不支持。多路复用简单来说就是一个 TCP 连接从单车道(不是单行道)变成了几百个双向通行的车道。
  3. HTTP/2 可以压缩 head,但是 HTTP/1.1 不行。
  4. HTTP/2 支持服务器推送,但 HTTP/1.1 不支持。

# 简单讲解一下 http2 的多路复用

# 简单版回答:

HTTP/2 复用 TCP 连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应。

举例来说,在一个 TCP 连接里面,服务器同时收到了 A 请求和 B 请求,于是先回应 A 请求,结果发现处理过程非常耗时,于是就发送 A 请求已经处理好的部分, 接着回应 B 请求,完成后,再发送 A 请求剩下的部分。

# 历史原因解释:

# 1、HTTP/1.0 版本

该版本主要缺点是,每个 TCP 连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。为了解决这个问题,需要使用  Connection: keep-alive  这个字段。

# 2、HTTP/1.1 版本

该版本引入了持久连接(persistent connection),即 TCP 连接默认不关闭,可以被多个请求复用,不用声明 Connection: keep-alive。还引入了管道机制(pipelining),即在同一个 TCP 连接里面,客户端可以同时发送多个请求。这样就进一步改进了 HTTP 协议的效率。

虽然 1.1 版允许复用 TCP 连接,但是同一个 TCP 连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为"队头堵塞"(Head-of-line blocking)。

# 3. http/2 版本

重新定义底层 http 语义映射,允许同一个连接上使用请求和响应双向数据流。同一域名只需占用一个 TCP 连接,通过数据流(Stream)以帧为基本协议单位,从根本上解决了问题,避免了因频繁创建连接产生的延迟,减少了内存消耗,提升了使用性能

# HTTP 和 HTTPS 的区别有哪些?

HTTPS = HTTP + SSL/TLS(安全层)

  1. 协议不同:HTTP 协议是超文本传输协议,信息是明文传输的,HTTPS 则是具有安全性的 SSL 加密传输协议。
  2. 端口不同, HTTP 使用 80 端口,HTTPS 使用 443 端口。
  3. 证书,HTTPS 的 CA 证书一般需要购买(但也有免费的),HTTP 不需要证书。
  4. HTTP 协议连接很简单,是无状态的;HTTPS 协议是有 SSL 和 HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 更加安全。
  5. HTTP 较快,HTTPS 较慢。

# http/https 协议

  • http1.0中,默认使用的是短连接,也就是说,浏览器每进行一次 http 操作,就建立一次连接,任务结束就中断连接,譬如每一个静态资源请求时都是一个单独的连接,

    • 无法复用链接,完成即断开,重新慢启动和 TCP 3 次握手
    • head of line blocking: 线头阻塞,导致请求之间互相影响
  • http1.1 起,默认使用长连接,使用长连接会有这一行Connection: keep-alive,在长连接的情况下,当一个网页打开完成后,客户端和服务端之间用于传输 http 的 tcp 连接不会关闭,如果客户端再次访问这个服务器的页面,会继续使用这一条已经建立的连接

    • host 字段指定对应的虚拟站点
    • 新增功能:
      • 断点续传
      • 身份认证
      • 状态管理
      • cache 缓存
        • Cache-Control
        • Etag
  • http2.0

    • 多路复用(即一个 tcp/ip 连接可以请求多个资源)
    • 首部压缩(http header 编码压缩,减少体积)
    • 二进制分帧(在应用层跟传送层之间增加了一个二进制分帧层,改进传输性能,实现低延迟和高吞吐量)
    • 服务器端推送(服务端可以对客户端的一个请求发出多个响应,可以主动通知客户端)
    • 请求优先级(如果流被赋予了优先级,它就会基于这个优先级来处理,由服务器对请求的响应设置优先级权重,决定需要多少资源来处理该请求。)
  • https: 较为安全的网络传输协议

    • 证书(公钥)
    • SSL 加密
    • 端口 443

注意: keep-alive 不会永远保持,它有一个持续时间,一般在服务器中配置(如 apache),另外长连接需要客户端和服务器都支持时才有效

# Websocket

WebSocket 是 HTML5 提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于 TCP 传输协议,并复用 HTTP 的握手通道。浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。

WebSocket 的出现就解决了半双工通信的弊端。它最大的特点是:服务器可以向客户端主动推动消息,客户端也可以主动向服务器推送消息。

WebSocket 原理:客户端向 WebSocket 服务器通知(notify)一个带有所有接收者 ID(recipients IDs)的事件(event),服务器接收后立即通知所有活跃的(active)客户端,只有 ID 在接收者 ID 序列中的客户端才会处理这个事件。

WebSocket 特点的如下:

  • 支持双向通信,实时性更强
  • 可以发送文本,也可以发送二进制数据‘’
  • 建立在 TCP 协议之上,服务端的实现比较容易
  • 数据格式比较轻量,性能开销小,通信高效
  • 没有同源限制,客户端可以与任意服务器通信
  • 协议标识符是 ws(如果加密,则为 wss),服务器网址就是 URL
  • 与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

Websocket 的使用方法如下:

在客户端中:

// 在index.html中直接写WebSocket,设置服务端的端口号为 9999
let ws = new WebSocket('ws://localhost:9999')
// 在客户端与服务端建立连接后触发
ws.onopen = function() {
  console.log('Connection open.')
  ws.send('hello')
}
// 在服务端给客户端发来消息的时候触发
ws.onmessage = function(res) {
  console.log(res) // 打印的是MessageEvent对象
  console.log(res.data) // 打印的是收到的消息
}
// 在客户端与服务端建立关闭后触发
ws.onclose = function(evt) {
  console.log('Connection closed.')
}

单工通信,收发方固定;半双工通信,收发方不固定,但同一时刻只能一方发一方收;全双工通信,双发可同时收发。

各种通信和网络书籍上对于全双工和半双工的经典举例一般就是电话和对讲机。

如果以生活中的例子来说,可以理解为只能过一辆车的桥,和双向各一车道的桥。前者同时只有一个方向可以过,后者来回双向都可以同时过。

# 接口如何防刷

防刷一般分两种:

  • 总调用次数受限制。这个一般是在后端做限制,单位时间内最多可调用次数。
  • 同一客户端次数限制。这个前端的一般使用是给接口调用加锁,在返回结果或者一定时间之后解锁。

1:网关控制流量洪峰,对在一个时间段内出现流量异常,可以拒绝请求 2:源 ip 请求个数限制。对请求来源的 ip 请求个数做限制 3:http 请求头信息校验;(例如 host,User-Agent,Referer) 4:对用户唯一身份 uid 进行限制和校验。例如基本的长度,组合方式,甚至有效性进行判断。或者 uid 具有一定的时效性 5:前后端协议采用二进制方式进行交互或者协议采用签名机制 6:人机验证,验证码,短信验证码,滑动图片形式,12306 形式

# 参考

  • https://github.com/Advanced-Frontend/Daily-Interview-Question/labels/%E7%BD%91%E7%BB%9C