前端--计网篇--(自己看)

11 阅读14分钟

计算机网络

1.HTTP的各个版本介绍?

目前HTTP协议一共有4大版本,HTTP1.0;HTTP1,1;HTTP2;HTTP3,先明确一个前提:HTTP 是应用层协议,本质是请求-响应模型,它自己不负责可靠性。真正负责可靠传输的是 TCP。TCP 提供的是一个有序、可靠的字节流,而 HTTP 是在这个字节流之上定义了消息边界,比如通过 Content-Length、空行分隔 header 和 body 等方式,去拆分出一个个完整的请求和响应。 在 http1.0 时代,其实最大的问题是连接成本太高。默认是短连接,一个请求就建一次 TCP。如果一个页面有很多资源,那就会频繁三次握手、四次挥手,还会反复触发 TCP 慢启动。高并发场景下,连接数很容易被打满,TIME_WAIT 也会很多。 到 http1.1,其实核心改进就是 keep-alive,默认复用 TCP 连接。这样可以减少握手开销,也避免频繁慢启动。同时引入 Host 头,解决了虚拟主机的问题。但它本质上还是串行响应模型。虽然有 Pipelining,但因为响应必须按顺序返回,前面一个慢了,后面都会卡住。这就是应用层的队头阻塞。也正因为这样,浏览器才会限制同域名大概 6 个 TCP 连接,用多连接并行来缓解阻塞问题。 到了 HTTP/2,就开始解决并发模型的问题了。它通过二进制分帧、多路复用、头部压缩,把多个请求放在同一个 TCP 连接里并发跑,每个请求对应一个 stream,请求和响应可以交错发送。这解决了应用层的队头阻塞。但后来发现,问题其实还没彻底解决。因为底层还是 TCP。只要 TCP 丢一个包,整个连接里的所有 stream 都要等重传,这就是传输层的队头阻塞。 所以到了 HTTP/3,干脆调整传输层了。它基于 QUIC,而 QUIC 是在 UDP 之上实现的一套可靠传输机制。自己去做拥塞控制、重传、流控,而且每个流是独立确认和重传的。这样一个流丢包,不会影响其他流。同时 QUIC 把 TLS 也整合进来,支持 0-RTT,进一步降低连接建立延迟。 所以从 1.0 到 3,其实是从“少建连接”,到“一个连接里并发跑多个请求”,再到“干脆把传输层也改掉”。

知识点引申

  1. HTTP 长连接的原理

    HTTP 是应用层协议,本身不负责连接管理。连接是 TCP 的能力。所谓 HTTP 长连接,本质是 TCP 连接不关闭,然后在这个 TCP 字节流里连续发送多个 HTTP 请求。 长连接的原因是在于TCP有一个保活机制,其原理就是通过在服务器端设置一个保活定时器,当定时器开始工作后就定时的向网络通信的另一端发出保活探测的TCP报文,如果接收到了ACK报文,那么就证明对方存活,可以继续保有连接;否则就是有故障。缺点就是:因为服务器需要一直保存和客户端的这条链接,因为是有状态的,那么在大量并发连接请求过来时,系统资源可能就不够了。

    扩展1:TCP慢启动:它的核心思想是发送方不知道网络容量,所以从小流量开始,通过指数增长的方式试探带宽,直到发生丢包或达到阈值再转为线性增长。这也是为什么短连接频繁建立会影响 HTTP 性能,因为每次都会重新触发慢启动。

  2. HTTP/2 为什么有时候不如 HTTP/1.1 快?

    HTTP/2 在理想网络环境下确实更优,但它把所有请求压在一个 TCP 连接上,弱网或丢包情况下会放大 TCP 的问题。 所有的请求是跑在一个TCP连接上的,一旦丢一个包,整个连接就会等这个包重传,所有的stream就会被卡住。而 HTTP/1.1 多个 TCP 连接是“物理并行”的 项目中遇到的,我们发现 HTTP/2 在大文件和小请求混合场景下,小请求会被拖慢,原本20ms左右的被拖慢到120ms左右。原因是所有流共享一个 TCP 连接,大文件会持续占用拥塞窗口。一旦丢包,整个连接速率下降,所有请求都会受影响。TCP的发送受这个限制-拥塞窗口(cwnd)和 发送缓冲区,说白话就是一次最多在空中飞多少数据包,-大文件不断占用发送窗口,把“在路上的数据位置”填满,小请求虽然逻辑上是独立的stream,但是底层还是排队进入同一个 TCP 发送队列,共享同一个拥塞窗口,这个时候丢包就会导致cwnd 减半,整个连接速率下降。

  3. 浏览器为什么限制同域名 6 个 TCP 连接?

    http1.1虽然支持 keep-alive,但一个连接里的响应基本还是顺序返回,前面慢请求会影响后面。浏览器为了提高并发,只能多开 TCP 连接。但连接又不能无限开,因为 TCP 建连有成本,而且会增加服务器压力和拥塞风险,所以浏览器做了一个平衡,一般限制在 6 个左右

  4. HTTP2 怎么去做性能优化?

    HTTP/1.1 时优化的核心是减少请求数,因为连接并发能力有限,请求会排队,所以我们会合并 JS、合并 CSS、用雪碧图、甚至做域名分片。但 HTTP/2 有多路复用,请求不再排队,但是关注RTT所以优化主要从“减少请求”转变为“优化请求时机”和“资源优先级”,预加载,优先首屏加载等方式。

2.缓存

关于缓存,其实我一开始也以为浏览器缓存就是强缓存和协商缓存。后来调试发现还有 memory cache、disk cache。因此从实现上看,一部分是存储层,比如内存缓存、磁盘缓存;另一部分是策略层,也就是 HTTP 头控制的缓存规则。 资源先存在内存或者磁盘里,但到底能不能用,是由 Cache-Control、ETag 这些字段决定的。
另外如果用了 Service Worker,它甚至可以直接拦截请求,缓存逻辑完全由我们控制。

浏览器缓存其实是有一个判断流程的。当再次请求一个资源时,浏览器会先判断是否命中强缓存,如果命中就直接使用本地资源;如果没命中,再进入协商缓存流程,请求服务器确认资源是否更新。因此,这里主要记一下缓存策略,强缓存和协商缓存。

强缓存:

强缓存的核心其实就一句话:在有效期内,浏览器连请求都不会发。判断依据主要是 Cache-Control,其次是 Expires,不过现在基本都用 Cache-Control。Expires 是 HTTP/1.0 的方案,它是一个绝对时间,依赖客户端本地时间,所以如果用户系统时间不准,会导致缓存异常。现在主要用的是 Cache-Control,比如 max-age 表示相对时间,更可靠。浏览器会根据响应时间 + max-age 算出是否过期。如果命中强缓存,浏览器直接从 memory cache 或 disk cache 读取资源,Network 面板会显示 200,但其实没有走网络

注意点:no-cache, no-store no-cache 它不是不缓存,而是可以缓存,但每次使用前必须去服务器验证。真正完全不缓存的是 no-store。no-cache 本质是强制进入协商缓存流程。

协商缓存:

协商缓存是当强缓存失效后,浏览器会带上 If-None-Match(Etag) 或 If-Modified-Since(Last-Modified) 去服务器验证资源是否更新。如果服务器判断没更新,就返回 304,浏览器继续用本地缓存;如果更新了,就返回 200 和新的资源。它的好处是减少带宽消耗,但仍然会产生一次RTT。

注意:为什么有 ETag 还需要 Last-Modified?以及哪一个更可靠

从准确性来说,ETag 更可靠,因为它基于内容生成标识,而 Last-Modified 只是时间,存在精度问题。但 Last-Modified 成本更低,只需要比较时间戳,在高并发或大文件场景下更轻量。所以很多服务端会两个都返回,浏览器优先使用 ETag 校验。

304到底发生了什么?

304 本质上是一次“缓存确认”, 当强缓存过期后,浏览器不会直接下载资源,而是带上之前保存的 ETag 或 Last-Modified 去问服务器这个资源有没有变,如果没有变化如果没变 → 返回 304(没有响应体),浏览器继续使用本地缓存。它减少的是传输内容,但仍然有一次 RTT。

代理缓存

浏览器缓存解决了减少客户端的请求次数,但是有个问题第一次请求还是会打到源服务器,或者大量用户同时访问怎么办? 因此这里引出了代理缓存,也就是在源服务器前,且位于边缘节点,减少源站的压力(降低延迟,抗流量洪峰),用副本换取吞吐,空间换时间,像CDN,反向代理缓存等

源服务器的缓存控制,在响应头中,加上cache-control,其中private 和 public,s-maxage: 表示缓存在代理服务器中可以存多久 客户端的缓存控制:请求头中加上,max-stale:过期几秒内可以;min-fresh:到期前几秒;only-if-cacheed,只接受代理缓存,不接受源服务器的返回,过期返回405 bad-gateway;

3. Https

Https 出现主要是因为Http是明文传输的,没有经过任何加密,而这些明文数据会经过WiFi、路由器、运营商、机房等多个物理设备节点,会出现中间人攻击,也就是说任意一个节点被监听,信息就会暴漏。

Https的本质就是将HTTP的数据包再通过SSL/TLS加密后传输,重点的SSL/TLS密码学操作。在这之前,先了解一下对称加密和非对称加密。

对称加密是指有一个密钥,用它可以对一段明文加密,加密之后也只能用这个密钥来解密得到明文。如果通信双方都持有密钥,且天知地知你知我知,绝对不会有别的人知道,那么通信安全自然是可以得到保证的。

非对称加密有两个密钥,一个是公钥,另一个是私钥。一般来说,公钥用来加密,这时密文只能用私钥才能解开。

这里从传输场景来看,客户端和服务端是不可能事先沟通好一个通信密钥出来,这样就会导致如果在传输过程中被别人监听到了,后续的所有加密都是没有用的,因此在握手阶段就会通过非对称加密验证服务器身份,那么,当客户端发起连接请求,服务端将公钥传输过去,客户端利用公钥加密好信息,再将密文发送给服务端,服务端里有私钥可以解密。但是,当服务端要返回数据,如果用公钥加密,那么客户端并没有私钥用来解密,而如果用私钥加密,客户端虽然有公钥可以解密,但这个公钥如果公开过,很有可能已经有人拿到,并不安全,所以这一过程只用非对称加密是不能满足的。因此需要对称加密+非对称加密一起使用,

1.服务端有非对称加密的公钥A1,私钥A2; 2.客户端发起请求,服务端将公钥A1返回给客户端; 3.客户端随机生成一个对称加密的密钥K,用公钥A1加密后发送给服务端; 4.服务端收到密文后用自己的私钥A2解密,得到对称密钥K,此时完成了安全的对称密钥交换,解决了对称加密时密钥传输被人窃取的问题 5.之后双方通信都使用密钥K进行对称加解密

虽然这样,但是依旧有一问题,就是依旧考虑中间人攻击,具有一对公私钥,在非对称加密阶段,替换程自己的公钥发送给浏览器,浏览器后续使用这个公钥进行加密对称密钥进行消息传输,这样消息依旧被窃取,主要原因就是客户端人无法确认收到的公钥是从服务端返回的--引入CA。

服务端在使用Https之前,先去CA申请数字证书,包含证书持有者,有效期,公钥等信息,将这个证书发送给客户端,客户端进行校验身份和有效期再进行后续的操作;但是中间人如果只更改证书中的公钥信息,客户端依旧不知道,因此又出现了数字签名。流程就是CA有自己的一对公私钥,在颁发证书的时候对证书明文信息进行哈希,随后用私钥进行加签,得到数字签名;明文和数字签名发送给客户端,客户端收到后分解明文和数字签名,并使用CA的公钥进行解签,得到Signature2,然后同样的使用证书中声明的哈希算法对明文进行哈希,如果计算的哈希和解签后的相等,表明可信没有被篡改。

4. Http 请求方法

http/1.1规定了以下请求方法:

  1. GET: 通常用来获取资源
  2. HEAD: 获取资源的元信息
  3. POST: 提交数据,即上传数据
  4. PUT: 修改数据
  5. DELETE: 删除资源(几乎用不到)
  6. CONNECT: 建立连接隧道,用于代理服务器
  7. OPTIONS: 列出可对资源实行的请求方法,用来跨域请求
  8. TRACE: 追踪请求-响应的传输路径
  1. GET 和 POST 的区别
  1. 从语义上来看,GET表示请求获取资源;POST 表示提交数据上传资源
  2. 最主要区别在于参数和幂等性方面,GET 一般放在URL中,POST放在请求体中,更适合传输敏感信息。GET是幂等的,POST可能会产生副作用。其次就是GET默认可缓存,POST不行,但是是否缓存是由请求头控制的。

引申1:URI 和 URL URI全称为(Uniform Resource Identifier), 也就是统一资源标识符,它的作用很简单,就是区分互联网上不同的资源。 URI: 协议名://host:port/path?query URI 只能使用ASCII, ASCII 之外的字符是不支持显示的,URI 引入了编码机制,将所有非 ASCII 码字符界定符转为十六进制字节值,然后在前面加个%;因此注意GET方法由于参数在URL中的,因此只能是ASCII码格式

4. Http 状态码

  1. 1xx: 表示目前是协议处理的中间状态,还需要后续操作。
  2. 2xx: 表示成功状态。
  3. 3xx: 重定向状态,资源位置发生变动,需要重新请求。
  4. 4xx: 请求报文有误。
  5. 5xx: 服务器端发生错误。

~

  1. 101: 协议切换时会出现,Http 升级成 websocket时
  2. 200: 成功状态码,响应体有数据; 201: 请求成功,服务器创建资源;204:与200相同,只是响应体没有资源;206响应部分内容
  3. 301,永久重定向,302:临时重定向,302浏览器不会做缓存优化;304 Not Modified: 当协商缓存命中时会返回这个状态码
  4. 400:服务器错;403:禁止访问;404:资源没找到;405:请求方法不被允许;408:服务器等待时间太长
  5. 500:客户端错;501:客户端请求的功能不支持;502:bad gateay,访问出错;503:服务器在忙

5. TCP 和 UDP

TCP的流量控制?

TCP 需要把发送的数据放到发送缓存区, 将接收的数据放到接收缓存区。流量控制所需要做的事情就是通过缓存区的大小来控制发送端的发送。