HTTP 连接是 HTTP 报文传输的关键通道。
持久连接(Persistent Connection)
HTTP 协议采用 请求-应答 模式:
在 HTTP/1.0 版本中,如果客户端浏览器支持 Keep-Alive ,那么就在 HTTP 请求头中添加一个字段 Connection: Keep-Alive
,当服务器收到附带有 Connection: Keep-Alive
的请求时,它也会在响应头中添加一个同样的字段来使用 Keep-Alive
。这样一来,客户端和服务器之间的 HTTP 连接就会被保持,不会断开(超过 Keep-Alive
规定的时间,意外断电等情况除外),当客户端发送另外一个请求时,就使用这条已经建立的连接。
在 HTTP/1.1 版本中,默认情况下所有连接都被保持,如果加入 Connection: close
才关闭。目前大部分浏览器都使用 HTTP 1.1 协议,也就是说默认都会发起 Keep-Alive 的连接请求了,所以是否能完成一个完整的 Keep-Alive 连接就看服务器设置情况。
注意事项:
Keep-Alive: timeout=5, max=100
,表示这个 TCP 通道可以保持 5 秒,且该长连接最多接收 100 次请求就断开❓ 使用长连接后,客户端和服务端如何知道本次传输结束呢?
Content-Length
指示的大小Transfer-Encoding: chunked
),这时候就要根据 chunked
编码来判断,chunked
编码的数据在最后有一个空 chunked
块,表明本次传输数据结束传输编码在 HTTP 的报文中,使用 Transfer-Encoding
首部字段进行标记,它就是指明当前使用的传输编码。
Transfer-Encoding
会改变报文的格式和传输的方式,使用它不但不会减少内容传输的大小,甚至还有可能会使传输变大,看似是一个不环保的做法,但是其实是为了解决某些特殊问题。
简单来说,传输编码必须配合持久连接使用,为了持久连接中,将数据分块传输,并标记传输结束而设计的。
Transfer-Encoding
在 HTTP/1.1 协议里,就只有 chunked
这个参数,标识当前为分块编码传输。
分块传输的规则:
分块传输编码 Chunked Transfer Encoding
Transfer-Encoding: chunked
表示分块传输数据,设置这个字段后会自动产生两个效果:
Content-Length
首部字段会被忽略我们以 Node.js 模拟分块传输:
const http = require('http');const server = http.createServer();server.on('request', (req, res) => {if (req.url === '/') {res.setHeader('Content-Type', 'text/html; charset=utf8');res.setHeader('Content-Length', 10);res.setHeader('Transfer-Encoding', 'chunked');res.write('Hello world!');setTimeout(() => {res.write('第一次传输');}, 1000);setTimeout(() => {res.write('第二次传输');}, 2000);}});server.listen(8010, () => {console.log('成功启动');});
当我们使用 chunked
进行分块编码传输的时候,传输结束之后,还有机会在分块报文的末尾,再追加一段数据,此数据称为拖挂(Trailer)。
拖挂的数据,可以是服务端在末尾需要传递的数据,客户端其实是可以忽略并丢弃拖挂的内容的,这就需要双方协商好传输的内容了。
在拖挂中可以包含附带的首部字段,除了 Transfer-Encoding
、Trailer
以及 Content-Length
首部之外,其他 HTTP 首部都可以作为拖挂发送。
内容编码和传输编码一般都是配合使用。我们会先使用内容编码,将内容实体进行压缩,然后通过传输编码分块发送出去。客户端接收到分块的数据,再将数据进行重新整合,还原成最初的数据。
默认情况下 HTTP 协议中每个传输层连接只能承载一个 HTTP 请求和响应,浏览器会在收到上个请求的响应后,再发送下个请求。
在使用持久连接的情况下,某个连接上消息传递类似于 请求1 -> 响应1 -> 请求2 -> 响应2 -> 请求3 -> 响应3
.
HTTP Pipelining(管线化)是将多个 HTTP 请求整批提交的技术,在传送过程中不需等待服务端的应答。使用 HTTP 管线化后,某个连接上的消息变成类似这样,请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3
。
注意事项: