du blog
Hello, welcome to my blog
http笔记
created: Feb 24 21updated: Jul 12 21

HTTP/0.9

http是基于TCP/IP协议的应用层协议. 它不涉及数据包的传输. 主要规定了客户端与服务端之间的通信格式. 默认使用80端口.

最早是1991年发布的0.9版本, 该版本极其简单, 只有一个命令 GET;

1 GET /index.html 2

上边的命令表示: tcp连接建立之后, 客户端向服务器请求网页 index.html

协议规定,服务器只能回应html格式的字符串, 不能回应别的格式

1 <html> 2 <body>Hello World</body> 3 </html> 4

服务器发送完毕之后,就关闭tcp连接

HTTP/1.0

简介

1996年5月, HTTP/1.0发布, 内容大大增加, 首先任何格式的内容都可以发送, 这使得互联网不仅能传输文字, 还能传输图像、视频、二进制文件。

其次,除了 GET 命令, 还引入了 POST 和 HEAD 命令, 丰富了浏览器与服务器交互的手段。

再次, HTTP请求和回应格式也变了, 除了数据部分, 每次通信必须包括头部信息(HTTP header), 用来描述元数据.

其它新增的功能还包括: 状态码(status code)、 多字符集支持、 多部分发送(multi-part type)、权限、 缓存(cache)、 内容编码(content-encoding)等。

请求格式

1 GET / HTTP/1.0 2 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) 3 Accept: */* 4

第一行是请求命令、路径、协议版本(HTTP/1.0), 后边是多行头部描述客户端的情况.

回应格式

1 HTTP/1.0 200 OK 2 Content-Type: text/plain 3 Content-Length: 137582 4 Expires: Thu, 05 Dec 1997 16:00:00 GMT 5 Last-Modified: Wed, 5 August 1996 15:55:28 GMT 6 Server: Apache 0.84 7 8 <html> 9 <body>Hello World</body> 10 </html> 11

回应的格式是: 头部信息 + 一个空行(/r/n) + 数据. 其中 第一行为 协议版本(HTTP/1.0) + 状态码 + 状态描述.

Content-Type 字段

关于字符的编码, 1.0版本规定, 头部信息必须使用 ASCII 码, 后边的数据可以是任意格式, 因此服务器回应的时候, 必须告诉客户端, 数据是什么格式的, 这就是 content-type 字段的作用,

下边是一些常用的 content-type 字段的值

1 text/plain 2 text/html 3 text/css 4 image/jpeg 5 image/png 6 image/svg+xml 7 audio/mp4 8 video/mp4 9 application/javascript 10 application/pdf 11 application/zip 12 application/atom+xml 13

这些数据类型统称为 MIME type, 每个值包括一级类型和二级类型, 之间使用斜杠分割.

除了预定义的类型, 也可以自定义类型:

1 application/vnd.debian.binary-package 2 application/json 3

MIME type 还可以在尾部使用分号, 添加参数:

1 Content-Type:text/html;charset=utf-8 2

上边的类型表示 发送的是网页, 且编码格式是utf-8;

客户端请求时, 可以使用 Accept 字段声明自己可以接受那些类型的数据格式.

1 Accept:*/* 2

上边的代码中, 客户端声明自己可以接受任何格式的数据.

MIME type 不仅用在http协议, 还可以用在告诉浏览器数据类型:

1 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 2 <!-- 等同于 --> 3 <meta charset="utf-8" /> 4

参考mdn: https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/meta

Content-Encoding 字段

由于发送的数据可以是任何格式的, 因此可以把数据压缩后再发送, Content-Encoding 字段说明数据的压缩方法.

1 Content-Encoding: gzip 2 Content-Encoding: compress 3 Content-Encoding: deflate 4

客户端在请求的时候, 可以使用 Accept-Encoding 字段说明自己可以接受哪些压缩方法.

1 Accept-Encoding: gzip, deflate 2

缺点

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

TCP连接的成本很高, 因为需要客户端和服务器三次握手, 并且开始时发送速率较慢, 所以HTTP1.0版本的性能较差, 随着网页加载的外部资源越来越多, 这个问题愈发突出.

为了解决这个问题, 有些浏览器在请求时, 使用了一个非标准的 Connection 字段

1 Connection: keep-alive 2

这个字段要求服务器不要关闭TCP连接, 以便其他请求复用, 服务器同样回应这个字段.

1 Connection: keep-alive 2

一个可复用的TCP连接就建立了, 直到客户端或者服务器主动关闭连接, 但是这不是标准的字段, 不同的实现的行为可能不一致, 因此不是根本的解决办法。

HTTP/1.1

1997年1月, HTTP/1.1版本发布, 只比1.0版本晚了半年, 它进一步完善了HTTP协议.

持久连接

1.1版本的最大变化就是引入了持久连接, 即TCP连接默认不关闭, 可以被多个请求复用, 不用声明 Connection:keep-alive.

客户端和服务器发现对方一段时间没有活动, 就可以主动关闭连接, 不过, 规范的做法是, 客户端在最后一个请求时, 发送Connection:close , 明确要求服务器关闭 TCP 连接.

1 Connection: close 2

目前, 对于同一域名, 大多数浏览器允许同时建立6个持久连接.

管道机制

1.1版本还引入了管道机制, 即在同一个TCP连接里, 客户端可以同时发送多个请求, 这样就进一步改进了 HTTP 协议的效率.

举例来说, 客户端需要请求两个资源, 之前的做法是, 在同一个TCP连接中, 先发送 A 请求, 然后等待服务器做出响应, 收到之后再发出 B 请求, 管道机制则是允许浏览器同时发出 A 和 B 请求, 但是服务器还是按照顺序, 先回应 A 请求, 完成后在回应 B 请求。

Content-Length字段

一个TCP连接现在可以传送多个回应, 势必要有一种机制, 区分数据包属于哪一个回应的, 这就是 Content-Length 字段的作用, 声明本次回应的数据长度.

1Content-Length: 3495 2

上边的代码告诉浏览器, 本次回应的长度是3495个字节, 后边的字节就属于下一个回应了.

在1.0版本中, Content-Length 字段不是必需的, 因为浏览器发现服务器关闭了TCP连接, 就表明收到的数据包已经全了

分块传输编码

使用 Content-Length 字段的前提条件是, 服务器发送响应之前, 必须知道回应数据的长度.

对于一些很耗时的动态操作来说, 这意味着服务器要等到所有的操作完成, 才能发送数据, 显然这样的效率不高, 更高的处理方法是, 产生一块数据, 就发送一块, 采用 "流模式" 取代 "缓存模式".

因此1.1版本规定可以不使用 Content-Length 字段, 而使用 "分块传输编码", 只要请求或者回应的头部信息有 Transfer-Encoding 字段, 就表明 回应将由数量未定的数据块组成.

1 Transfer-Encoding: chunked 2

每个非空的数据块之前, 都会有一个16进制的数值, 标识这个块的长度, 最后是一个大小为0的块, 表示本次回应的数据发送完毕了,下边是一个例子:

1 HTTP/1.1 200 OK 2 Content-Type: text/plain 3 Transfer-Encoding: chunked 4 5 25 6 This is the data in the first chunk 7 8 1C 9 and this is the second one 10 11 3 12 con 13 14 8 15 sequence 16 17 0 18 19 20 21

其它功能

1.1版本还新增了许多动词方法: PUT、 PATCH、 HEAD、 OPTIONS、 DELETE,另外, 客户端的请求信息新增了 Host 字段, 用来只有服务器的域名.

1 Host: www.example.com 2

有了Host字段, 就可以将请求发往一台服务器上的不同网站.

缺点

虽然1.1版本允许复用TCP连接, 但是同一个TCP连接里边, 所以的数据通信都是按照次序进行的, 服务器只有处理完一个回应, 才会进行下一个回应, 要是前边的回应特别慢, 后边就会有许多请求排队等着, 这就是 "队头阻塞"

为了避免这个问题, 只有两种方法: 一是减少请求次数, 二是同时多开持久连接, 这就导致了很多网页优化技巧, 比如合并脚本和样式表, 将图片嵌入css代码, 域名分片等, 如果HTTP协议设计的更好一些, 这些额外的工作是可以避免的.

SPDY协议

2009年, 谷歌公开了自行研发的SPDY协议, 主要解决HTTP/1.1效率不高的问题,这个协议在Chrome浏览器上证明可行之后, 就被当做HTTP/2的基础, 主要特性都在HTTP/2中得到继承.

HTTP/2

2015年HTTP/2f发布, 它不叫 HTTP/2.0 是因为标准委员会不打算再发布子版本了, 下一个新的版本将是HTTP/3

二进制协议

HTTP/1.1版本的头部信息肯定是文本(ASCII编码), 数据体可以是文本, 也可以是二进制。 HTTP/2则是一个彻底的二级制协议, 头部信息和数据体都是二级制, 并且统称为 "帧": 头部帧和数据帧.

二进制协议的一个好处是, 可以定义额外的帧, HTTP/2定义了近十种帧, 为将来的高级应用打好了基础. 如果使用文本实现这种功能, 解析数据将会变得非常麻烦, 二进制解析则方便很多.

多工

HTTP/2复用TCP连接, 在同一个连接里, 客户端和服务器都可以同时发送多个请求或者回应, 而且不用按照顺序一一对应, 这样就避免了"队头阻塞".

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

这样的双向、实时的通信, 就叫多工.

数据流

因为HTTP/2的数据包是不按照顺序发送的, 同一个连接里连续的数据包, 可能属于不同的回应, 因此必须要对数据包做标记, 指出它属于那个回应。

HTTP/2将每个请求或回应的所有数据包,称为一个数据流, 每个数据流都有一个独一无二的编号,数据包在发送的时候, 都必须标记数据流ID,用来区分它是属于哪一个数据流, 另外还规定, 客户端发出的数据流, ID一律为奇数, 服务器发出的 ID为偶数.

数据流发送到一半的时候, 客户端和服务器都可以发送信号(帧), 取消这个数据流, 1.1版本取消数据流的唯一方法就是关闭TCP连接,这就是说 HTTP/2可以取消某一次请求, 同时保证TCP连接还打开, 可以被其他请求使用.

客户端还可以知道数据流的优先级, 优先级越高, 服务器就会越早回应.

头部压缩

HTTP协议不带有状态, 每次请求都必须附上所有信息, 所以每次请求的很多字段都是重复的, 比如说 cookie和User Agent, 一模一样的内容, 每次请求都必须附带, 这会浪费很多带宽, 也影响速度.

HTTP/2对这一点进行了优化, 引入了头部压缩机制, 一方面, 头部信息使用 gzip或者compress压缩后再发送, 另一方面, 客户端和服务器同时维护一张头部信息表, 所有的字段都会存入这个表, 生成一个索引,以后就不用发送同样的字段了, 只发送索引号, 这样就提高了速度

服务器推送

HTTP/2允许服务器未经请求, 主动向客户端发送资源, 这叫做服务器推送(server push)。

常见的场景是客户端请求一个网页, 这个网页包含很多静态资源, 正常情况下, 客户端必须收到网页后, 解析HTML源码, 发现有静态资源, 再发出静态资源请求, 其实, 服务器可以预期客户端请求网页后, 很有可能会再次请求静态资源, 所以就主动吧这些资源随着网页一起发送给客户端了.

缺点

HTTP/2的一个主要特性是使用多路复用(multiplexing),因而它可以通过同一个TCP连接发送多个逻辑数据流。复用使得很多事情变得更快更好,它带来更好的拥塞控制、更充分的带宽利用、更长久的TCP连接————这些都比以前更好了,链路能更容易实现全速传输。标头压缩技术也减少了带宽的用量。HTTP/2解决了HTTP的队头拥塞(head of line blocking)问题,客户端必须等待一个请求完成才能发送下一个请求的日子过去了。

采用HTTP/2时,浏览器一般会在单个TCP连接中创建并行的几十个乃至上百个传输。如果HTTP/2连接双方的网络中有一个数据包丢失,或者任何一方的网络出现中断,整个TCP连接就会暂停,丢失的数据包需要被重新传输。因为TCP是一个按序传输的链条,因此如果其中一个点丢失了,链路上之后的内容就都需要等待。这种单个数据包造成的阻塞,就是TCP上的队头阻塞(head of line blocking)。

随着丢包率的增加,HTTP/2的表现越来越差。在2%的丢包率(一个很差的网络质量)中,测试结果表明HTTP/1用户的性能更好,因为HTTP/1一般有六个TCP连接,哪怕其中一个连接阻塞了,其他没有丢包的连接仍然可以继续传输。