# HTTP的传输编码
# 持续连接
持久连接通俗来讲,就是长连接,英文叫 Persistent Connection
,其实按字面意思理解就好了。
在早期的 HTTP 协议中,传输数据的顺序大致分为发起请求、建立连接、传输数据、关闭连接等步骤,而持久连接,就是去掉关闭连接这个步骤,让客户端和服务端可以继续通过此次连接传输内容。
这其实也是为了提高传输效率,我们知道 HTTP 协议是建立在 TCP 协议之上的,自然有 TCP 一样的三次握手、慢启动等特性,这样每一次连接其实都是一次宝贵的资源。为了尽可能的提高 HTTP 的性能,使用持久连接就显得很重要了。为此在 HTTP 协议中,就引入了相关的机制。
在早期的 HTTP/1.0 协议中并没有持久连接,持久连接的概念是在后期才引入的,当时是通过 Connection:Keep-Alive
这个头部来标记实现,用于通知客户端或服务端相对的另一端,在发送完数据之后,不要断开 TCP 连接,之后还需要再次使用。
而在 HTTP/1.1
协议中,发现持久连接的重要性了,它规定所有的连接必须都是持久的,除非显式的在报文头里,通过 Connection:close
这个首部,指定在传输结束之后会关闭此连接。
实际上在 HTTP/1.1
中Connect
这个头部已经没有 Keep-Alive
这个取值了,由于历史原因,很多客户端和服务端,依然保留了这个报文头。
长连接带来了另外一个问题,如何判定当前数据发送完成。
对于非持续连接,浏览器可以通过连接是否关闭来界定请求或响应实体的边界;而对于持续连接,这种方法显然不奏效。 有时,尽管我已经发送完所有数据,但浏览器并不知道这一点,它无法得知这个打开的连接上是否还会有新数据进来,只能傻傻地等了。
这时有2种方案:
# Content-length
计算实体长度,并通过头部告诉对方。浏览器可以通过 Content-Length
的长度信息,判断出响应实体已结束。
但是Content-length
引入的新问题:
由于 Content-Length
字段必须真实反映实体长度,但是对于动态生成的内容来说,在内容创建完之前,长度是不可知的。这时候要想准确获取长度,只能开一个足够大的 buffer
,等内容全部生成好再计算。
但这样做一方面需要更大的内存开销,另一方面也会让客户端等更久。
我们需要一个新的机制:不依赖头部的长度信息,也能知道实体的边界——分块编码(Transfer-Encoding: chunked
)就这么产生了。
# Transfer-Encoding
Transfer-Encoding
在最新的 HTTP/1.1
协议里,就只有 chunked
这个参数,标识当前为分块编码传输
。
分块编码传输既然只有一个可选的参数,我们就只需要指定它为 Transfer-Encoding:chunked
,后续我们就可以将内容实体包装一个个块进行传输。
分块传输的规则:
每个分块包含一个 16 进制的数据长度值和真实数据。
数据长度值独占一行,和真实数据通过 CRLF(\r\n) 分割。
数据长度值,不计算真实数据末尾的 CRLF,只计算当前传输块的数据长度。
最后通过一个数据长度值为 0 的分块,来标记当前内容实体传输结束。
用一段nodejs代码来感受下:
const net = require('net');
net.createServer(function(sock) {
sock.on('data', function() {
sock.write('HTTP/1.1 200 OK\r\n');
sock.write('Transfer-Encoding: chunked\r\n');
sock.write('\r\n');
sock.write('b\r\n');
sock.write('01234567890\r\n');
sock.write('5\r\n');
sock.write('12345\r\n');
sock.write('0\r\n'); //代表结束
sock.write('\r\n');
});
}).listen(9090, '127.0.0.1');
在浏览器输入:http://localhost:9090
显示:0123456789012345
如果去掉最后的代表结束的2句,那么,在浏览器网络里可以看到,接口一直处于pending状态。如果加个延时,响应也会在延时时间后才会结束。
# http2.0
HTTP 2.0
最大的特点: 不会改动 HTTP
的语义,HTTP
方法、状态码、URI
及首部字段,等等这些核心概念上一如往常,却能致力于突破上一代标准的性能限制,改进传输性能,实现低延迟和高吞吐量。而之所以叫 2.0
,是在于新增的二进制分帧层。
可以这么简单区分这几个版本:
2.0 的分帧传输是在TCP和HTTP之间加了一个帧的概念,帧可以
乱序传输
。多个帧可以同时传输
,速度快(多个buffer同时传)1.1 的chunked是分块传递,一块一块传输,顺序(块可以理解成一个buffer,一个一个传)
1.0 是字节传输,一字节一字节连续传输(没有buffer)
因为1.1没有分帧的规定,所以用了Transfer-Encoding: chunked
这种机制。而http2
之后因为有了原生的分帧功能,所以就没有必要用Transfer-Encoding: chunked
,直接发一个大的http请求出去,协议会自动分帧加快传输效率。
参考: