前言
对于一个前端来说,http 应该是除了 js, css, html 之外,我们接触的最多的东西,当然也可能是平常最不关心的一个知识点。因为大部分场景你都不需要太了解它,只要知道500找后端,400能不能想办法把锅甩给后端就对了(大误)。但是,仅仅是知道几个状态码是不够的(至少你的面试官不会满意==!),本文的主要内容就是对一些常见的 http 知识点进行盘点和一些简单的说明,希望在居家旅行,日常开发还是面试跳槽能有一点帮助~
什么是 http
首先我们要知道什么是http, http 全称是 hyperText transfer protocol 超文本传输协议。是一个基于请求与响应模式的、无状态的、应用层协议。http 协议是tcp/ip 协议簇的子集。
当然在一次完整的http 请求过程中, 仅有http 协议是不够的,还会涉及到 tcp 协议(http3.0 开始的 udp协议),ip 协议, dns, cookie 等关联知识点,让我们了解下吧。

tcp
tcp协议位于传输层,提供可靠,字节流的传输服务。为了方便数据传输 tcp 协议把数据切割以报文段为单位的数据包,也就是字节流的传输。 为了保证可靠性,tcp 协议采用了三次握手建立连接, 之所以采用三次握手,是确保通讯双方都拥有发送和接收能力,以建立可靠的传输连接。 在tcp协议中,建立好了连接准备好了数据下面就要发送出去,接下来就会交给网络层的ip 协议去处理。
ip协议
ip协议是一个网络层的协议,ip协议主要作用是在复杂的网络环境中选择合适的路径保证数据可以传输到目标地址。为了达到这个目的我们就要用到常说的ip地址和mac地址。ip地址表明在了节点在网络上的地址,mac地址是硬件网卡被分配的地址。这里还会用到一个 arp协议,主要作用就是通过把ip反查到mac地址。通常情况下,一次通讯不会发生在一个局域网内,当通过arp协议无法查到对应的mac地址时只好交给下一个网络设备继续查找,直到找到目的地址。这个过程称为路由选择。这个过程就需要通过mac地址找到下一跳的设备位置。
dns
我们平常使用中,并不会直接使用ip地址,而是使用域名访问互联网资源。但是前面说到,http 通讯是需要ip地址的。这里就需要dns 服务。简单来说,dns 就是提供一个域名与ip地址相互查找的服务。当输入一个域名时,首先最开始做的就是通过dns服务查找到对应的ip地址。dns解析可以理解为 dns 服务器存了一张 域名与 ip 的对应表,当接收到一个解析请求时候,先去查自己有没有这个对应关系,如果有就返回解析结果,没有的话就向上层转发。这里顺便说下在dns解析过程中一个安全问题,叫做dns劫持。他的主要原理就是利用木马程序或者某个无良运营商强行修改dns映射关系,让你打开baidu的域名映射到某hub的ip上。
cookie
在描述http协议上有个字眼叫做无状态,它的含义就是在两次http 通讯中是不相关的,服务器并不会保存上次通讯的信息。但是对于一些需要登录检验用户身份的站点这样是无法满足需求的,为了解决这个问题,出现了 cookie. cookie 是保存在客户端的一小段明文信息。有的请求响应时会通过set-cookie 字段告诉浏览器保存一些数据,浏览器保存这个数据,并在之后的请求带上,借此达到状态的关联。
以下是cookie 字段:
- expires 设置 cookie 的过期时间,值是一个 utc 时间
- max-age 设置 cookie 最长的生存时间,浏览器接受到 cookie 后,在过 max-age 时间后失效。max-age 与 expires 同时存在时候,以 max-age 为准。因为时区问题服务器时间和客户端时间可能时不准确的,expires 可能并不准确。
- domain 设置 cookie 对应的域名。只有在对应域名或者子域名的请求上会带上这个 cookie。例如 domain 值是 baidu.com, 那 www.baidu.com, map3.baidu.com 都可以使用这个 cookie.
- path 指定 cookie 的路径,只对相应的路径以及子路径下,才能使用这个 cookie
- secure 指定 cookie 只有在 https 加密协议下才可以使用
- httpOnly 指定 cookie 只可用于 http 请求,不能被 js 等脚本语言处理
- SameSite 指定三方跨站请求是否可以携带cookie,主要用来防止csrf攻击的。可以配置三个值 Strict, lax, none 其中 strict 最严格,要求请求与当前网页url相同才可以带上cookie。lax 则宽松一些允许a连接,预加载,get 表单带上cookie。none 则是完全不处理,都可以带上cookie。

http header
http 报文中必定包含http 首部,这些首部字段提供了客户端和浏览器要如何处理或者响应请求需要的信息。根据header 字段的使用情况可以把http header 分为四个类别:
- 常规头部字段,请求和响应都会用到。常见的有: cache-control, connection, date
- 请求头部字段,客户端向服务器发送请求一些补充请求的字段。常见的有: accept, cookie, referer, user-agent
- 响应头部字段, 服务器向客户端响返回响应报文时的补充字段。常见的有:age, location, server
- 实体头部字段,表示资源主体的主要信息。常见的有: content-length, content-type, last-modified, etag
https
上面我们了解了一些 http 的知识点,下面我们来说说https。 https 可以说很常见了,大部分的网站都使用了 https 协议。 那为什么要用https呢? 首先,看下http 存在的问题:
- http 传输明文传输,内容可能被窃听。
- 不验证通讯双方身份
- 不检验报文完整性,可能报文被篡改
为了解决 http 本身存在的这些问题。引入了 ssl/tls 层来处理这些问题。这种包裹了 ssl 的 http 协议被称为 https。 在【图解http】这本书中有一句很经典的描述: http + 加密 + 认证 + 完整性校验 = https。

-
http传输过程中是可能被窃听甚至篡改,也就是平常说的http劫持问题。https通过对http报文主体进行加密,保证就算被劫持也只是看到无意义的内容,但是加密无法保证内容不被篡改。
-
理论上 http 通讯是不进行身份校验的,https 通过引入证书的机制来对通讯双方进行身份的校验。证书中包含了这个证书包含公钥,有效时间等明文信息,以及使用这些明文信息的摘要,再使用CA机构的私钥对摘要进行加密获得的 签名。一般由受信任的第三方 CA 机构签发。处于性能考虑,一般身份验证过程中是使用非对称加密,身份认证后,通过非对称加密传输对称加密的密钥,后续通讯就可以使用对称加密进行了,下面是较为详细的过程:
- 服务商向 CA 机构提交证书认证申请。CA 机构在认证信息后会签发证书。
- 在获取到证书后,在建立 https连接时,服务器会把证书下发给浏览器。
- 浏览器在操作系统中寻找已内置的受信任的证书发布机构CA,与服务器发来的证书中的颁发者CA比对。如果找到了浏览器就会取出颁发者CA的公钥,然后对服务器发来的证书里面的签名进行解密。同时对证书明文信息进行同样的签名算法,比对签名是否一致来校验。
- 校验通过后,浏览器生成一个对称加密的密钥,使用证书中的公钥进行加密发送给服务端,服务端使用私钥解密,之后的通讯加密就可以使用这个密钥进行通讯了。
-
前面说到,https 的报文依然是存在被篡改的风险的,因此 https 还需要进行报文完整性校验。主要原理就是利用数字签名,对报文使用哈希算法获得一段摘要,使用密钥对在摘要进行加密一起传输给通讯方。通讯方接收后,先解密获得摘要,再对报文主体使用相同算法获得一段摘要,比较摘要是否一致来确定完整性。
下图是完整的一次https建立连接的过程(tls1.2):

http缓存
对于前端来说,有一个最大的要求就是更快。那么怎么才能更快呢?这时候就会遇到老朋友: http 缓存。通过 cache-control, expires 等头部信息来告诉浏览器帮忙缓存资源一段时间,这样不用每次拉资源都要麻烦服务器处理了。对缓存来说,我们可以分为两种: 强制缓存和协商缓存。
强制缓存
强制缓存的意思就是在浏览器发出请求之前,先判断本地是否资源缓存,如果有缓存 在 http 头部信息中包含了 expires 和 cache-control,并且当前还未过期,这时候浏览器会强制使用本地的缓存。当 expires 和 cache-control 同时存在时,浏览器会以cache-control 为准, 因为 expires 是一个绝对时间可能并不准确。
协商缓存
在 cache-control 失效后,并不是说不能使用缓存了,这时候还可以看两个头部字段:1 Etag 服务器根据文件生成的标识 2 Last-modified 表示资源上次修改的时间。如果有 etag 则在请求头加上 If-None-Match , 有 Last-modified 则在请求头加上 If-Modified-Since. 由服务器决定是 304 还是 200。这一过程称为协商缓存。
这里提到
etag和Last-modified来决定是否需要缓存。相对来说 ,通过 etag 判断会更为准确,因为etag是通过算法得出的文件唯一值,只要文件内容不改变就不会改变 etag 的值。而Last-modified 的精度只能到秒级别。但是etag 也存在一定问题,例如etag是根据文件内容哈希计算得到的,这本身就需要消耗一定的性能。其次对于分布式存储系统来说(cdn),还要保证计算etag 的结果一致性。
HTTP/2
http/2 也就是超文本传输协议第2版。它是基于SPDY协议进行开发设计的。 (SPDY最开始由谷歌发起,SPDY协议修改了HTTP的请求与应答在网络上传输的方式,使用了多路复用,服务器推送,压缩http 头部信息等手段来达到降低网页的加载时间优化目的)。现在基本上的主流浏览器都支持了 http/2, http/2 也逐渐开始普及。下面就简单看下http/2 这几个厉害的升级。
多路复用
这里的多路复用是指 tcp 多路复用,多个请求由一个 tcp 连接来处理。这样同一个域名上的多个请求只需要经历一次tcp握手,大大减少了通讯的成本。同时还可以不受限制的并发请求。这一切都依赖于 http2 两个非常重要的概念:帧和流。
在http1.1中,数据的传输默认是基本于文本的,而HTTP2全部采用二进制来传输数据。把一个 二进制的最小传输单位称为 1 帧。同时在每一帧上都会有标识,这样数据就可以乱序的传输,接收到后只要按标识排序就不会出现乱序问题。多个帧进行传输就形成了流,http2 允许一个tcp连接上有多个流的存在,也就支持了并发。
在 http1.1 也有复用tcp连接的实现也就是持久连接: keep-alive。同时http1.1 还可以通过pipelining技术实现一个请求发出后立刻发出下一个请求而不用等待上一个请求响应,但是由于http1.1 是基于序列和阻塞机制的,即使是并发的发出请求,但是响应是串行的。所以浏览器默认都关闭了支持pipelining,我们现在看到的浏览器中http1.1请求的并发都是通过同时建立几个tcp连接来处理的,这也就是为什么浏览器会限制并发数。
http2 的多路复用使得请求变成一件廉价的事情,这也使得前端之前的一切优化手段不再合适,例如为了减少请求使用雪碧图,合并js,css 资源等。

头部压缩
如何你去看一个现在的请求会发现,http 头部信息是比较长的,可能在一次通讯中,数据只有少量,而实际请求的头部占用大部分资源。 http2 为了优化这一部分采用一种名为hpack头部压缩算法。简单说,hpack 算法主要原理是浏览器和服务器共同维护一个静态字典,保存常用的头部名称或者名称和值,有了这个静态字典一些头部字段就可以用一个字符表示。 同时还可以为每一个 HTTP/2 连接建立动态字典,例如一些 cookie, use-agent 等固定不变的头部字段可以保存到动态字典中。内容采用哈夫曼编码进行压缩。

服务器推送
在传统的 http 请求中,你请求什么它就返回什么。例如一个 html文件,浏览器在处理资源时会按顺序一个一个建立连接下载资源。于是http2 支持了服务器推送的功能。在你请求一个 html 文件时,服务器同时把html文件,css, js 等静态资源一次返回,从而减少连接的消耗。
http3
当我们正在等待http2 快点普及时,http3 已然到来。 http3 刚开始叫做 http over QUIC,顾名思义,http3是基于 quic 协议来设计的。而这个 quic 协议是谷歌发明的一种新的网络传输协议。简单看下quic 的出现是为了解决哪些问题。
- 在传统的 http 中使用传输协议是 tcp, 首先tcp是面向连接的,建立连接需要往返握手过程,同时现在大部分场景使用的还是 https 还需要 TLS握手,使得一次简单的 http 请求会把大量时间花费在建立连接握手上。
- tcp连接为了保证可靠性使了用序列号来标识数据的顺序,数据必须按照顺序处理,如果前面的数据丢失,后面的数据就算到达了也不会通知应用层来处理。也就是 TCP 的队头阻塞问题。
为了解决tcp 在通讯上的这些问题, quic 协议干脆直接抛弃了 tcp, 采用了 udp 协议,从根本上解决问题。目前 quic 协议已经在chrome, firefox, edge 上得到支持。更多的 quic 入门知识大家请戳这个文章 ===> QUIC协议原理分析
