面试必备-计算机网络-HTTP

面试必备-计算机网络-HTTP

HTTP/0.9

主要用于传输较小的HTML文件,整体设计比较简单

特点:

  • 只有一个请求行,没有请求体
  • 服务器返回也没有返回头信息
  • 内容使用ASCII字节流传输

HTTP/1.0

在web发展的过程中,出现了一些新的需求,浏览器中也出现了js,css,图片音视频等不同类型文件,所以HTTP1.0诞生.

特点:

  • 加入了请求头和响应头:承载诸如:文件编码,文件类型,压缩方式,语言版本
  • 加入状态码,告知浏览器处理情况
  • 加入Cache机制,缓存用户已下载的资源
  • 加入了UA,用于统计客户端基本信息

请求头/响应头解析

请求头:

accept: text/html
accept-encoding: gzip, deflate, br  // 浏览器支持的压缩方式
accept-Charset: ISO-8859-1,utf-8 //支持的编码格式
accept-language: zh-CN,zh //接受的语言类型

响应头

content-encoding: br
content-type: text/html; charset=UTF-8

如果返回信息中包含浏览器无法支持的编码或压缩方式,则会出现报错.

HTTP/1.1

针对资源变多,和h1.0固有问题,h1.1进行了一些改进

特点:

  • 改进持久连接,使用keepalive复用同一个tcp连接
  • 加入cookie
  • 管道化:可以批量发送请求,服务器还是要按顺序回复浏览器请求
  • 浏览器为每个域名最多同时维护 6 个 TCP 持久连接;
  • 使用 CDN 的实现域名分片机制。

主要问题

TCP慢启动

建立tcp后,浏览器最开始会已一个很慢的速度(14kb)进行传输,然后逐渐加快直到达到理想速度,慢启动是减少网络拥塞的一种策略.

同时开启多条TCP,TCP之间会竞争宽带

开启多个tcp连接后,如果发现宽带不足,各个tcp就会减慢网速,如果里面包含很多媒体资源,或者脚本资源,多条tcp之间无法知晓哪个链接的优先级更高,这样会影响关键资源的下载.

HTTP/1.1 队头阻塞的问题

keepalive 虽然可以共用一个tcp管道,但是在管道中同一时间只能处理一个请求,如果前面的请求没有结束,其他请求就处于阻塞状态.

幂等问题

常用求头

content-length/Transfer-Encoding

在不同协议下会有不同表现形式

1.0:

1.0 是字节传输,一字一字节连续传输(没有buffer)

1.1:

在HTTP 1.1中引用了Chunk机制(Http Streaming)。具体来说,就是在响应的头部加上Transfer-Encoding:chunked属性,其目的是告诉客户端,响应的Body是分成了一块块的,块与块之间有间隔符,所有块的结尾也有个特殊标记。这样,即使没有Content-Length字段,也能方便客户端判断出响应的末尾

如果报文中包含Transfer-Encoding: chunked首部, 那么Content-Length将被忽略.

2.0:

2.0 的分帧传输是在TCP和HTTP之间加了一个帧的概念,帧可以乱序传输。多个帧可以同时传输,速度快(多个buffer同时传)

注意:Content-Length如果存在且生效, 必须是正确的, 否则会发生异常.(大于实际值会超时, 小于实际值会截断并可能导致后续的数据解析混乱)

状态码

301(永久重定向)/302(临时重定向)

缓存问题:

301资源永久移动到新的位置,并且这个响应默认情况下会被缓存,只有在第一次的时候,才会去真正的发起第一个请求,后面的都会被缓存起来,直接跳转到 redirect 的请求

302默认情况下不缓存

搜索引擎问题

301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;

302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址

使用场景

301比较常用的场景是使用域名跳转。

302用来做临时跳转 比如未登陆的用户访问用户中心重定向到登录页面。

304

击中协商缓存

参考:

HTTP2.0

根据上面的主要问题,tcp慢启动和tcp之间宽带竞争是tcp的固有问题,http的问题在于队头阻塞.

所以h2换了一个思路去解决这些问题:

  • 对于tcp的问题,我们无法替换掉tcp但是可以通过另一条思路来解决,就是一个域名只是用一条tcp通道,这样慢启动也只会有一次,也避免了多个tcp之间竞争宽带.
  • 对于队头阻塞,http实现了并行请求,不需要等待其他请求的完成

多路复用

服务器端接收到这些请求后,会根据自己的喜好来决定优先返回哪些内容,比如服务器可能早就缓存好了 index.html 和 bar.js 的响应头信息,那么当接收到请求的时候就可以立即把 index.html 和 bar.js 的响应头信息返回给浏览器,然后再将 index.html 和 bar.js 的响应体数据返回给浏览器。之所以可以随意发送,是因为每份数据都有对应的 ID,浏览器接收到之后,会筛选出相同 ID 的内容,将其拼接为完整的 HTTP 响应数据。

HTTP/2 使用了多路复用技术,可以将请求分成一帧一帧的数据去传输,这样带来了一个额外的好处,就是当收到一个优先级高的请求时,比如接收到 JavaScript 或者 CSS 关键资源的请求,服务器可以暂停之前的请求来优先处理关键资源的请求

多路复用实现

整体过程:

  • 浏览器准备好数据,请求行,请求头,如果是post请求还会有请求体,并将数据交给二进制分帧层
  • 二进制分帧层将数据转换为带有ID编号的帧,然后通过协议栈发送到服务器
  • 服务器收到帧数据后会将相同编号的帧合并成一条完成的请求信息
  • 服务器根据请求内容产生相应的响应行,响应头,响应体,并交给二进制分帧层
  • 同样二进制分帧层也会将响应数据转换为带ID编号的帧返回给浏览器
  • 浏览器收到数据后返回给对应请求

服务器推送

推送方式?

  • nginx配置:http2_push
  • 返回头配置 Link: </styles.css>; rel=preload; as=style
    • 前提需要开启 http2_push_preload on;

PUSH缓存问题?

假如浏览器有了缓存,主动推送后仍然会使用本地缓存,导致主动推送会浪费宽带

一般第一次访问时才推送缓存,然后设置cookie,带了cookie就不做服务端推送

头部压缩

每次请求的头部其实都是一些重复字段,比较浪费流量

整理流程

  • 所以在传输过程中,客户端和服务器会使用首部表来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送
  • 首部表在h2的连接存续期内始终存在,由客户端,服务器共同渐进更新
  • 每个新的首部键值对要么被追加到当前表末尾,要么替换表中之前的值

下图中,第二次请求只需要发送差异数据

其他特性

  • 请求可以设置优先级,chrome顺序 html>css>js,不过有兼容问题,IE不支持
💡
TCP阻塞:在丢包情况下,后面的请求要等待重传,可能比http1.1还慢

参考:

QUIC

TCP队头阻塞

TCP最初是为了单连接而设计,所以可以吧TCP当成两台计算机之间的虚拟管道.

这个过程就是一端按照顺序将数据通过管道传输到另一端,另一端将这些数据包组合成原始数据,这样就完成了数据传输.

如果在传输过程中,如果有一个包丢了,那么整个tcp会处于暂停状态,等待这个丢失的包重传.

单个数据包丢失而造成的阻塞称为TCP队头阻塞.

在http2中,多个请求是跑在同一个TCP管道中,如果其中任意一路数据流中出现了丢包,那么就会阻塞整个TCP中的请求.

在http1.1中,浏览器为每个域名开启了6个tcp,如果其中一个tcp阻塞,其他5个tcp还会继续.

因此,在丢包率高(≥2%)的情况下,http2的传输效率反而比http1.1低

RTT

网络延迟又称为 RTT(Round Trip Time)。我们把从浏览器发送一个数据包到服务器,再从服务器返回数据包到浏览器的整个往返时间称为 RTT(如下图)。RTT 是反映网络性能的一个重要指标。

建立TCP需要多少个RTT呢?

  • 建立TCP请求需要3次握手,需要花费1.5个RTT
  • 如果还有TLS连接,根据不同版本需要1-2个RTT
💡
使用 TLS 1.2 需要两次往返( 2-RTT )才能完成握手,然后才能发送请

也就是每次请求需要3-4个RTT,如果浏览器和客户端的物理距离比较远,一个RTT大概10ms,那么一次请求的RTT的时间为30-40ms;如果物理距离比较远,一个RTT大概100ms,那么一次请求就需要300-400ms,这样明显是很慢的.

TCP协议僵化

  • 中间设备的僵化:这些中间设备有很多种类型,并且每种设备都有自己的目的,这些设备包括了路由器、防火墙、NAT、交换机等。它们通常依赖一些很少升级的软件,这些软件使用了大量的 TCP 特性,这些功能被设置之后就很少更新了。
  • 操作系统也是导致 TCP 协议僵化的另外一个原因。因为 TCP 协议都是通过操作系统内核来实现的,应用程序只能使用不能修改。通常操作系统的更新都滞后于软件的更新,因此要想自由地更新内核中的 TCP 协议也是非常困难的

QUIC协议

通过上图我们可以看出,HTTP/3 中的 QUIC 协议集合了以下几点功能。

  • 实现了类似 TCP 的流量控制、传输可靠性的功能。虽然 UDP 不提供可靠性的传输,但 QUIC 在 UDP 的基础之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些 TCP 中存在的特性。
  • 集成了 TLS 加密功能。目前 QUIC 使用的是 TLS1.3,相较于早期版本 TLS1.3 有更多的优点,其中最重要的一点是减少了握手所花费的 RTT 个数。
  • 实现了 HTTP/2 中的多路复用功能。和 TCP 不同,QUIC 实现了在同一物理连接上可以有多个独立的逻辑数据流(如下图)。实现了数据流的单独传输,就解决了 TCP 中队头阻塞的问题。

实现了快速握手功能。由于 QUIC 是基于 UDP 的,所以 QUIC 可以实现使用 0-RTT 或者 1-RTT 来建立连接,这意味着 QUIC 可以用最快的速度来发送和接收数据,这样可以大大提升首次打开页面的速度。

参考: