第 5 篇讲 body 边界时提过 chunked,这一篇展开讲。问题背景是:Content-Length 要求发送前就知道总长度,但很多场景做不到——动态生成的内容、流式响应、数据库游标逐行读。这时候就得用分块传输。

chunked 的核心:每块自带长度

Transfer-Encoding: chunked 的意思是「body 被分成多块,每块前面标明长度,最后用 0 长度块结束」。接收方不用等全部到位,边读边处理。

chunked 的传输结构:逐块发送,边到边处理

一个 chunked body 的格式:

1a\r\n            ← 这块 26 字节(十六进制 1a)
(26 字节数据)\r\n
0c\r\n            ← 这块 12 字节(十六进制 0c)
(12 字节数据)\r\n
0\r\n             ← 0 长度块,body 结束
\r\n              ← 空行收尾

关键点:

  • 每块以十六进制长度行开头,后跟 \r\n
  • 长度行后面是实际数据,再跟 \r\n
  • 最后一个块长度为 0,表示 body 结束,后面再跟一个空行。

这种结构让接收方逐块读取:读到长度行知道这块多大,读完这块数据,继续读下一块长度行,直到读到 0,结束。整个过程中,发送方根本不知道总长度——它发一块算一块。

chunked 解决了什么场景

chunked 主要用于「发送前不知道总长度」的场景:

chunked 的典型应用场景

  • 动态生成内容:模板渲染、数据聚合,输出长度不确定。
  • 流式响应(SSE):ChatGPT 的打字机效果——服务器一边生成一边推,不等的全部生成完。
  • 数据库游标:逐行读数据库,边读边发,不用全部缓存到内存。
  • 大文件转码:视频转码边转边发,不用等整个文件转完。

没有 chunked,这些场景只能先在内存里攒齐所有数据、算出总长度、再一次性发——内存占用大、用户等待久。chunked 让「边生产边消费」成为可能。

chunked 和 SSE 的关系

SSE(Server-Sent Events,text/event-stream)几乎总是配合 chunked 工作。因为 SSE 的本质是「服务器保持连接开着,持续推消息」,而持续推消息的前提是「不知道什么时候结束」——这正是 chunked 的场景。

SSE 流式响应:chunked 让服务器边生成边推

SSE 的连接用 chunked 保持打开,每条消息是一个 chunk,格式是 data: {...}\n\n。服务器想推多少推多少,连接一直开着,直到服务器主动关闭。所以你在抓包里看 SSE 流,看到的不是一次完整响应,而是一连串 chunk 在持续到达。

chunked 和 Content-Length 不能并存

这是硬规则:一个 body 只能用一种边界方式。要么 Content-Length(已知总长),要么 Transfer-Encoding: chunked(流式),不能两个都有。

如果同时出现,规范规定以 chunked 为准(chunked 优先),但实际实现可能不一致,别这么干。服务器决定用哪种:静态文件用 Content-Length,动态/流式用 chunked。

Trailer 头:chunked 的彩蛋

chunked 模式有个普通模式没有的能力——Trailer 头。普通响应所有头都在 body 前面发完;chunked 模式下,body 结束后还能跟几个头(叫 trailer),用于发送「整个 body 处理完才知道」的信息。

比如一个流的完整性校验和(MD5),只有整个流算完才能算出来。普通模式没法发(header 已经发完了),chunked 模式可以把它放在 trailer 里,body 结束后跟着发。

不过浏览器对 trailer 的支持很差,实战少见,知道有这么个东西就行。

取舍与边界

chunked 有几个值得注意的边界:

  • HTTP/2 干掉了 chunked。HTTP/2 用二进制帧传输,body 自然就是分帧的,不需要 chunked 这套文本格式。HTTP/2 里 Transfer-Encoding: chunked 被禁止使用。所以 chunked 是 HTTP/1.1 的产物,HTTP/2 用更底层的 DATA 帧替代。
  • chunked 影响缓冲。chunked 鼓励「小块多发」,但每个 chunk 都有 \r\n 等开销,块太小会有大量元数据浪费。实践中块大小通常几 KB。
  • 中间件可能缓冲 chunked。有些代理会把 chunked 响应完整缓冲(攒齐)再转发,这会破坏「流式」的效果。用 SSE 时要确认中间链路不缓冲。

收束:chunked 是 HTTP/1.1 的流式方案

chunked 解决了「不知道总长度时怎么传输」,让动态生成、流式响应、SSE 成为可能。但它是 HTTP/1.1 文本格式的产物,HTTP/2 用二进制帧替代了它。理解 chunked,你就理解了 HTTP/1.1 为什么能做流式,以及为什么 SSE 在 HTTP/2 里依然有效(虽然底层不是 chunked 了)。

下一篇讲范围请求——一个大文件,能不能只下载其中一段?断点续传怎么实现的?这就是 Range 请求的事。


关于十三Tech

我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。

我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。

如果你想继续跟完这套「图解 HTTP」,欢迎关注公众号 「十三Tech」。后续会按 URL 与报文、连接与传输、缓存与协商、安全与边界、HTTP/2 与 HTTP/3 这条线更新。

十三Tech公众号二维码