目录

互联网的基础协议 HTTP

HTTP 协议中文全称 「超文本传输协议」,是互联网的基础协议,目前最新版本为 HTTP 2.0。

HTTP 是基于 TCP/IP 协议的 应用层协议,主要规定了客户端与服务器端之间通信的格式,默认使用80端口。

HTTP 是无连接无状态的。

HTTP/0.9

1991年发布,该版本只有一个命令 GET

1
GET /index.html

以上命令表示 TCP 连接建立后,客户端向服务器端请求网页 index.html。

协议规定,服务器只能回应 HTML 格式的字符串,不能回应其他格式的数据,当服务器发送数据完毕,TCP连接就关闭。

HTTP/1.0

1996年5月发布,对0.9版进行大幅度增强。

在这一版本中,对发送内容限制取消,任何格式都可以发送,不仅仅是字符串,还能传输图像、视频、二进制文件等等。

除了 GET 还引入 POSTHEAD,丰富了客户端与服务器交互的手段。

HTTP请求和响应的格式也发生变化,在数据之外,每次通信都必须包含头信息(HTTP header),用来描述元数据。还增加了状态码、字符集支持、多部分发送(multi-part type)、权限认证(authorization)、缓存(cache)、内容编码(content encoding)等。

缺点

HTTP/1.0 的主要缺点是,每个 TCP 连接只能发送一个请求,发送完毕,连接就关闭,如果还要请求其他资源,就必须再次新建一个连接。

TCP 连接的新建是一个成本很高的过程,需要客户端与服务端进行三次握手,并且开始时发送速率较慢,这造成 HTTP/1.0 性能较差,随着网页内容越来越丰富,所要使用的外部资源越来越多,这个问题就愈发突出。

为了解决这个问题,有些浏览器在请求时加入了一个非标准头信息字段。

1
Connection: keep-alive

这个头信息字段要求服务器不要关闭 TCP 连接,以便其他请求能复用,服务器会回应这个字段,一个可复用的 TCP连接就建立了,直到客户端或服务器端主动关闭连接,但这不是标准字段,不同的实现行为可能带来不一致的行为。

HTTP/1.1

1997年1月,HTTP/1.1发布,这是目前最流行的版本,它对 HTTP/1.0 协议进行了进一步的完善。

这一版本中持久连接成为规范,既 TCP 连接默认不关闭,可以被多个请求复用,不用客户端发送要求保持连接的头信息。当客户端和服务器端发现对方一段时间没有活动,那就可以主动关闭连接,目前对于同一个域名,大多数浏览器允许同时建立 6 个持久连接。

1.1 引入管道机制,在之前的版本中客户端请求两个资源,在同一个 TCP 连接里面,先发送A请求,等待服务器做出响应后再发送B请求,而管道机制允许浏览器同时发送A请求和B请求,服务器依然按照顺序回应。

当一个 TCP 连接可以响应多个请求是,必须有一种机制来区分数据包到底属于哪一个请求,因此加入了 Content-Length 响应头,用来声明本次响应的数据长度。

1
Content-Length: 4999

以上头信息告诉客户端,本次响应的长度是 4999 个字节,超出部分就属于下一个请求。

使用 Content-Length 的前提是服务器发送响应前必须知道数据的长度。对于一些耗时操作,这意味着服务器要等到所有操作完成才能发送数据,显然这样效率不高。

因此 1.1 规定可以不使用 Content-Length ,而使用分块传输编码,只要请求或响应的头信息有 Transfer-Encoding 字段,就表明响应将由数量未定的数据块组成,这种方式就是产生一块数据,就发送一块,由「流模式」(stream)取代 「缓存模式」(buffer)。

1
Transfer-Encoding:chunked

当数据块发送给客户端时,会有一个 16 进制的数值,表示这个块的长度,当这个值为0时表示本次响应的数据已经发送完毕。

当网络速度较慢是可以明显感觉这种现象,网页信息是逐渐加载出现,而非从白屏突然到网页展现。

1.1 版本还新增了许多动词请求方法 PUTPATCHHEADOPTIONSDELETE

另外,客户端请求头新增了 Host 字段,用来指定服务器域名,有了 HOST 字段,就可以将请求发送到同一台服务器的不同网站,为虚拟主机的兴起打下了基础。

缺点

HTTP/1.1 虽然允许复用 TCP 连接,但是同一个 TCP 连接中,所有数据通信都是按次序进行的。服务器只有处理完一个请求后,才会进行下一个请求的处理。要是前面的请求处理特别慢,后面的请求就会一直处于排队等待状态,这称之为「队头阻塞」(Head-of-line blocking)。

为了避免这个问题,有两种方法来解决:

  1. 减少请求数。
  2. 同时多开持久连接。

因此出现很多网页优化技巧,如合并脚本和样式表,将图片嵌入CSS代码、雪碧图、静态资源CDN、域名分片(domain sharding)等等。

HTTP/2

2009年,谷歌公开了其自行研发的 SPDY 协议,这个协议主要解决 HTTP/1.1 效率不高的问题。

该协议在 Chrome 浏览器上证明可行后,被当做 HTTP/2 的基础,主要特性都在 HTTP/2 中继承。谷歌在2015年9月宣布移除 SPDY 的支持,拥抱HTTP/2,并在 Chrome 51 中生效。

2015年,HTTP/2 发布,去掉后面的小数位是因为标准委员会不打算发布其子版本,一个版本将是 HTTP/3

二进制协议

HTTP/1.1 的头信息肯定是文本(ASCII编码),数据体可是文本也可是二进制。HTTP/2 则是一个彻底的二进制协议,头体皆为二进制,且统称为「帧」(frame): 头信息帧与数据帧。

二进制协议的好处是可以定义额外的帧,HTTP/2 定义了约10种帧,为未来的高级应用打下了基础。如果使用文本实现此功能,解析数据将变得特别麻烦,二进制解析会方便得多。

多工

HTTP/2 将 TCP 连接复用,在一个连接里,客户端和浏览器都可以同时发送多个请求或者响应,而且不用按照顺序进行,这样就避免了「队头阻塞」。接上例,依然有A和B两个请求,服务器同时收到两个请求,先处理A请求,但发现处理过程非常耗时,于是将请求A已经处理的部分响应给客户端,接着处理B请求,完成B请求的响应后再处理A请求剩余部分。

这种方式叫做多工(Multiplexing)。是双向的,实时通信处理信息方式。

数据流

HTTP/2 的数据包不是按序发送,同一个连接中连续的数据包,可能分属不同请求,所以必须对数据包进行标记,以指出其属于哪个请求。

HTTP/2 将每个请求或响应的数据包称为一个数据流(stream)。 每个流都有其独一无二的编号ID,用于区分它的从属。另外还规定客户端发送的数据流,编号ID全部为奇数,服务器端的全部为偶数。

数据流在发送过程中,客户端和服务器端都可以发送信号(RST_STREAM帧),取消这个数据流。HTTP/1.1 取消数据流的唯一方法是关闭 TCP 连接,这就是说,HTTP/2 可以取消请求同时保证 TCP 连接存活,以使 TCP 连接可以被其他请求使用。

客户端还可指定数据流的优先级,越高优先级,服务器会越早回应。

头信息压缩

HTTP 协议是无状态的,每次请求都需要附加上所有信息。因此对于同一个网站的许多请求附加信息都是重复的,如 CookieUser Agent,这样浪费很多带宽同时也影响速度。

HTTP/2 对此进行了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用 gzipcompress 压缩后再发送。客户端和服务器端同时维护着一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高了速度。

服务器推送

HTTP/2 允许服务器未接到请求就主动向客户端发送资源,称为服务器推送(server push)

常见场景是客户端请求一个网页,网页中包含很多静态资源,正常请求,客户端收到网页后解析 HTML 发现静态资源,再发出静态资源请求。

此时,服务器其实可以预期到客户端要请求静态资源,所以可以把资源随同网页主动推送给客户端。

截至 2015 年末,主要的浏览器的最新版本已经支持 HTTP/2 这一协议。其中: Google Chrome、Mozilla Firefox、Microsoft Edge 和 Opera 已支持 HTTP/2,并默认启用。 Internet Explorer 自 IE 11 开始支持 HTTP/2,并预设激活。

一次普通的 HTTP 的生命周期

  1. 客户端先向 DNS 服务器请求获取域名指向的真实 IP 地址。
  2. 获取到真实 IP 地址后向对应的服务器请求建立 TCP/IP 连接。
  3. 发送 HTTP 协议请求包给服务器。
  4. 服务器接收到请求包后调用自身服务进行处理。
  5. 服务器返回 HTTP Response (响应)。
  6. 客户端收到响应后解析渲染
  7. 客户端确认收到所有内容后断开连接。