HTTP分块编码传输
date
icon
slug
status
tags
type
password
summary
HTTP的长连接和短连接
HTTP协议是应用层协议,它是建立在TCP协议之上的。而TCP协议在传输数据之前,需要先通过三次握手来建立连接,并且由于TCP的慢启动的特性,使得一开始不会满负载传输,在不断尝试后才会提高负载。因此便提出了复用TCP的连接的需求,那样将会节约很多时间和资源。
在最早的HTTP/1.0中是不支持长连接的,在每次HTTP响应结束后,都要求必须要结束连接,这就是短连接。
在HTTP1/1.1中在Header中新增了Connection字段,取值为 close 和 keep-alive,close就是对应的HTTP/1.0时期的短连接,而默认则都是keep-alive,即长连接,除非特别声明使用close,否则均为长连接。
如何判断传输是否完成
在HTTP/1.0时不存在这个问题,只要结束连接即可视为传输完成,但在HTTP1/1.1复用TCP连接时,边出现了问题,我要怎么知道使用同一个TCP连接的某一个请求是否响应完成。
而在持久连接中,我们为了判断当前请求是否结束,我们向响应头中增添了一个Content-Length字段,这个字段用于告诉浏览器等客户端,响应体有多大,在读取到一定的数据后即可认为相应结束。

利用这个字段我们就可以很好的判断响应是否结束,但也因此我们必须要确保Content-Length的大小和实际响应大小要完全一致,如果Content-Length大于实际相应就会导致浏览器一直处于Pending状态,而Content-Length小于实际响应就会导致响应被错误截断,可能会发生意想不到的情况。
在早期的互联网,提前算出响应的大小并放在响应头中是完全可以做到的,但在互联网不断的发展下,相应的内容可能是动态生成的,或是无法提前算出长度。
比如,一个很大的文件,如果你要算出其长度则需要将其先读取到内存中,才能获取到整个文件的大小,但这样则必须确保内存大小足够,但实际上只是为了统计大小,实在有点浪费资源。
又或者,我现在要返回的内容是在不断生成的,我可能正在使用ffmpeg来处理视频,处理的视频文件在不断生成,为了算出响应大小,唯一的办法只能等待文件全部生成完毕然后读取到内存来计算,但这样耗费的时间不说,同样也面临着上面的问题。
分块编码传输 chunked
为了解决以上的问题,在HTTP1/1.1中还增加了一个请求头Transfer-Encoding,在HTTP1/1.1中唯一的取值为chunked。
分块传输的规则:
- 每个分块包含一个 16 进制的数据长度值和真实数据。
- 数据长度值独占一行,和真实数据通过 CRLF(\r\n) 分割。
- 数据长度值,不计算真实数据末尾的 CRLF,只计算当前传输块的数据长度。
- 最后通过一个数据长度值为 0 的分块,来标记当前内容实体传输结束。
由于分块编码传输将数据分为长度很短的数据块,并且以0作为结束,那么我们就不需要使用Content-Length来告知浏览器响应何时结束,只需要在读取到结束分块后拼接数据。
而由于分块编码传输的分块特性,分块编码传输建立在TCP连接复用的基础上,所以这也是在HTTP1/1.1中才提出的原因。
分块编码传输的服务端实现
在实习的过程中,由于使用的gofiber来做后端,而在fiber的文档中对分块编码传输的帮助又少之又少,最后是在相关的issue中找到,在下面记录一下代码实现。
🤗 [Question]: How to stream large file for download ?
Updated Apr 1, 2024