HTTP 报文就是首部一堆行 + 一个空行 + 一串字节。 没有长度前缀、没有魔数、没有校验,全靠空行分段。
第二章看的是收发双方的对话流程。 这一节开始往报文的肚子里看,后面五节全是它的变形:压缩、分块、多段、范围、协商。
一、三段结构,只有三样东西
① 报文首部:第一行(请求行或状态行)加若干首部行,每行以 CRLF 结尾; ② 空行:一个光秃秃的 CRLF,标志首部结束;③ 报文主体:可以没有,也可以有好几 MB。 Demo 里 111 字节的响应,首部占了 95 字节(86%),主体只有 12 字节。 短报文里首部才是大头,这是第六章要讲六十多个首部字段的原因,也是 HTTP/2 要压首部的原因。
二、纯文本的代价
人能读懂是优点:抓包直接看、telnet 手打、写个 socket 就能造。 本课所有 Demo 都是靠这一点才跑得起来的。 代价是边界全靠约定:接收方必须逐字节找那个空行,找到之前不知道首部有多长。 这让流式解析变麻烦,也让压缩变低效。 更麻烦的是它没有「这是第几个请求」的编号, 所以一个连接上没法交错多个请求——这就是 2.7.2 节队头阻塞的根。
三、书里的术语已经过时
这本书用的是 RFC 2616(1999 年)的术语:报文首部、报文主体、实体。
2014 年的 RFC 7230 把这套词换了:entity 被删,只留 message body 与 payload。
读这本书时需要做一次术语翻译。功能没变,只是叫法换了。
下一节会用 MD5 实测说明「报文主体」和「实体主体」到底差在哪。
四、动手 Demo:量一份最小响应:首部与主体各占多少
服务端回一个 12 字节的正文,把整段响应按空行切开,分别量首部、空行、主体三段的字节数。
body = u'hello 报文'.encode()
with H.server_multi(resp, keepalive=False) as port:
s = socket.create_connection((u'127.0.0.1', port), timeout=2.0)
s.sendall(_req())
got = H.read_all(s)
head, rest = got.split(b'\r\n\r\n', 1)
print(len(got), len(head), len(rest))
实跑输出:
--------------------------------------------------------------------
报文就是「首部一堆行 + 空行 + 一串字节」
--------------------------------------------------------------------
整段响应 111 字节
报文首部(含首行) 95 字节(86%)
空行 2 字节
报文主体 12 字节
文本 HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: 12\r\nCo…
十六 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d 0a 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 65 78 74 2f 70 6c 61 69 6e 3b 20 63 68 61 72 73 65 74 3d 75 74 66 2d 38 0d 0a 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 31 32 0d 0a 43 6f …
报文(message)= 报文首部 + CRLF + 报文主体。就这么简单。
没有长度前缀、没有魔数、没有校验——**纯文本靠空行分段**。
所以只要有一方算错边界,后面全乱,这也是它后来被二进制帧取代的原因。
整段响应 111 字节、报文首部(含首行)95 字节(86%)、报文主体 12 字节。
首部是主体的八倍——这个比例在短报文里很正常。
十六进制那行能看到 0d 0a 成对出现:每个首部行末尾一个,
最后一个 0d 0a 0d 0a 就是「空行」这个分隔符。
所以「空行」不是零字节,它就是两个字节的 CRLF。这一点在算 Content-Length 时经常被漏掉。
30 秒实验
- 把正文改成 0 字节,看响应变成什么样(首部还在,空行之后什么都没有)。
- 把
resp里多加两条首部,重跑,看首部占比涨到多少。 - 用
telnet或裸 socket 手打一个请求,亲眼看一下响应字节流。
常见误区
- 「空行是零个字节」——它是两个字节的 CRLF。
- 「报文一定有主体」——可以没有,比如 204、304、HEAD 的响应。
- 「首部越多越安全」——每个首部都要在每跳上传输,短请求里首部就是主要开销。
自测三问(点开对答案)
1. HTTP 报文的三段结构? —— 报文首部(首行 + 首部行)、空行(CRLF)、报文主体。 2. 为什么短报文里首部占比极高? —— 首部有固定的十几行,主体可以只有几个字节,比例自然被首部吃掉。 3. 纯文本格式带来什么麻烦? —— 边界靠约定、解析要逐字节找空行、没有请求编号所以不能交错,也难压缩。报文拆开了。下一节把请求和响应摆在一起,看它们怎么对称。
- ☐ (本节完,下一节 3.2 请求报文及响应报文的结构)