Appearance
HTTP 协议
2026 大纲 六(五)2 HTTP 协议。万维网的整体结构与 URL 在 WWW 万维网,三报文握手本身的机制在 TCP 连接管理。
速查
| 定位 | 面向事务(一系列信息交换是不可分割的整体,要么全完成、要么一次都不进行)、面向文本的应用层协议,用 TCP 端口 80(HTTPS 443) |
| 🔴 三句话各说一件事 | ① HTTP 使用面向连接的 TCP;② 协议 HTTP 本身是无连接的(交换报文前不需要先建立 HTTP 连接);③ 协议 HTTP 是无状态的。"HTTP/1.1 有持久连接所以不是无连接协议"是错的——持久说的是底下那条 TCP 连接不立即关闭 |
| 为什么故意设计成无状态 | 简化服务器设计,使服务器更容易支持大量并发请求。每个请求都能被独立处理,任何一台服务器都能接手任何一个请求——这是负载均衡与水平扩容的前提 |
| 🔴 取一个文档是 2 个 RTT 不是 3 个 | 三报文握手的前两个报文完成后(即经过一个 RTT),客户就把 HTTP 请求报文作为握手第三个报文的数据发出去,所以第三个握手报文不额外占 RTT |
| 四个 RTT 计数公式(1 个 HTML + | 非持久串行 |
| 🔴 恒定的那个 2 躲不掉 | 是取 HTML 本身的开销(建连 1 + 请求响应 1)——不拿到 HTML 就不知道页面引用了哪些对象 |
| 🔴 持久不一定比非持久快 | 持久省的是建连 RTT,并行省的是排队轮数,优化维度不同。非持久 + 多条并行完全可能快过持久非流水线。代价是并行要在服务器上分配多份连接缓存与变量 |
| 🔴 持久连接不限于同一个页面 | 只要这些文档都在同一个服务器上就能复用这条连接 |
| 🔴 流水线解决"请求要等",队头阻塞剩下"响应要排队" | HTTP/1.1 流水线允许客户连发请求,但服务器必须按先后顺序排队逐个发回响应;真正把响应并行化的是 HTTP/2 |
| 幂等性看服务器最终状态 | 执行 DELETE 重复执行会返回 404,但状态没变,仍幂等 |
| 🔴 "POST 比 GET 安全"要限定 | POST 只是参数不进 URL(不进历史、日志、Referer);报文体在 HTTP 下同样明文,抓包一样能看到。传输安全靠 HTTPS |
| 🔴 304 不是错误 | 属 3xx,表示资源未修改、可用本地缓存,不带实体主体;但它省不掉那一个 RTT,仍是一次完整往返 |
| 🔴 401 与 403 | 401 = 你还没证明你是谁(缺鉴权);403 = 知道你是谁但不给(授权不足) |
| 🔴 Cookie 没有让 HTTP 变成有状态协议 | 状态是客户端每次重新携带的,服务器仍不主动保存客户身份。有状态的是建在 HTTP 之上的应用 |
Host 首部在 1.1 里是必须的 | 一个 IP 上可放多个虚拟主机,服务器要靠它才知道客户要哪个站点——这也是 1.0 不支持虚拟主机的原因 |
一、请求方法
| 方法 | 作用 | 请求体 | 幂等 | 安全(不改服务器状态) |
|---|---|---|---|---|
| GET | 获取资源 | 无 | 是 | 是 |
| POST | 提交数据(如表单) | 有 | 否 | 否 |
| PUT | 上传/替换资源 | 有 | 是 | 否 |
| DELETE | 删除资源 | 无 | 是 | 否 |
| HEAD | 同 GET,但只返回首部、不返回实体 | 无 | 是 | 是 |
PUT /user/3 做 5 次和做 1 次、用户 3 的最终内容一样;POST /user 每次都新建一条记录,做 5 次就多 5 条——这就是幂等与不幂等的分界。GET 与 POST 的其余差别:参数在 URL 查询字符串还是请求体里,前者受 URL 长度限制(协议本身没规定上限,是实现的限制)且可以被缓存,后者一般不缓存。
二、报文结构
两类报文都由开始行 + 首部行 + 空行 + 实体主体四部分组成,区别只在开始行:请求报文是请求行(方法 URL 版本),响应报文是状态行(版本 状态码 短语)。请求报文一般没有实体主体(GET 就没有),响应报文也可能没有(如 304)。
由于 HTTP 面向文本,报文中每个字段都是 ASCII 码串、长度都不确定,靠每行末尾的 CR+LF 划界——这与 TCP/IP 首部那种定长二进制字段是完全不同的设计。
两个报文样例:四个部分实际长什么样(想看清那个空行卡在哪一行时展开)
请求报文(GET 没有实体主体,所以空行之后就没有内容了):
GET /index.html HTTP/1.1
Host: www.example.com
Connection: keep-alive
User-Agent: Mozilla/5.0
Accept: text/html第 1 行是请求行(方法 URL 版本),第 2~5 行是首部行,最后那个空行是首部与实体主体的分界——它必须存在,哪怕后面什么都没有。
响应报文:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
<html>...</html>第 1 行是状态行(版本 状态码 短语),随后是首部行,空行之后才是实体主体。Host 首部在请求里是 HTTP/1.1 强制要求的,Content-Length 则告诉对方实体主体有多长——面向文本的报文没有定长字段,实体的边界只能靠它(或分块编码)来定。
三、状态码
首位数字表示类别:1xx 信息性(收到了,继续)/2xx 成功(收到、理解、接受)/3xx 重定向(东西不在这,或你手上那份还能用)/4xx 客户端错误(你的请求有问题)/5xx 服务器错误(请求没问题,是我这边坏了)。
| 状态码 | 含义 | 要点 |
|---|---|---|
| 100 Continue | 收到请求的初始部分,可继续发送 | 大请求体先探路 |
| 200 OK / 201 Created | 成功/资源已创建 | 201 通常是 POST、PUT 的结果 |
| 301 Moved Permanently | 永久重定向 | 搜索引擎更新索引,客户端可缓存这次跳转 |
| 302 Found | 临时重定向 | 下次仍访问原地址 |
| 304 Not Modified | 资源未修改,可用缓存 | 不带实体主体,是优化不是错误 |
| 400 Bad Request | 请求语法错误 | — |
| 403 Forbidden | 服务器拒绝请求 | 服务器懂你的请求,但不给 |
| 404 Not Found | 资源不存在 | — |
| 500 Internal Server Error | 服务器内部错误 | — |
| 502 / 503 | 网关收到无效响应/服务暂时不可用 | 502 出问题的是上游;503 强调"暂时" |
四、非持久连接与持久连接
HTTP/1.0 非持久:每请求一个对象就建一条 TCP 连接、收到响应立刻关闭;代价除了每对象
| 方式 | TCP 连接数 | RTT 个数 |
|---|---|---|
| 非持久 · 串行 | ||
| 非持久 · | ||
| 持久 · 非流水线 | 1 | |
| 持久 · 流水线 | 1 |
四种方案的总时间逐步算(想确认自己能把每个 RTT 的来历说清时展开)
某网页含 1 个 HTML 文件和 8 个引用对象。RTT = 100 ms,链路速率 10 Mb/s,HTML 10 kb,每个对象 100 kb,忽略 DNS 解析与服务器处理时间。
第 1 步:先把传输时间单独算出来,它对四种方案都一样。RTT 数与文件大小无关、传输时间与 RTT 无关,两者可以分开算再相加;先把不变量提出来,后面只比 RTT 个数(注意 10 Mb/s = 10 000 kb/s):
第 2 步:非持久串行。 9 个对象(含 HTML)每个都要"建连 1 + 请求响应 1",一个不落:
第 3 步:非持久 + 5 条并行。 HTML 必须先单独取(不知道有哪些对象就没法并行),花 2 个 RTT;剩下 8 个对象分成
第 4 步:持久非流水线。 连接只建一次,那 8 个建连 RTT 全省了;但请求必须串行,8 个来回一个不能少:
第 5 步:持久流水线。 8 个请求连着发出去、服务器连着回,全部压进一个来回:
| 方案 | TCP 连接数 | RTT 个数 | RTT 时间 | 总时间 |
|---|---|---|---|---|
| 非持久 · 串行 | 9 | 18 | 1800 ms | 1881 ms |
| 非持久 · 5 并行 | 9 | 6 | 600 ms | 681 ms |
| 持久 · 非流水线 | 1 | 10 | 1000 ms | 1081 ms |
| 持久 · 流水线 | 1 | 3 | 300 ms | 381 ms |
最值得注意的是第 2 行:非持久 + 5 条并行(681 ms)竟然快过持久非流水线(1081 ms)——持久省的是建连 RTT,并行省的是排队轮数,两者优化的维度不同,所以"持久一定比非持久快"是错的。真正的组合拳是持久 + 流水线(381 ms),或 HTTP/2 的单连接多路复用。代价也要一并记住:并行方案的 9 条 TCP 连接要在服务器上分配 9 份缓存与变量,而持久流水线只占 1 条。
五、版本演进:一条追问链
| 特性 | HTTP/1.0 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|---|
| 连接 | 非持久(默认) | 持久(默认) | 持久 + 多路复用 | 基于 QUIC |
| 传输层 | TCP | TCP | TCP | UDP |
| 请求方式 | 串行 | 流水线(响应仍须排队) | 多路复用(响应可并行发回) | 多路复用 |
| 首部 | 面向文本,不压缩 | 面向文本,不压缩 | 二进制分帧 + HPACK 压缩 | QPACK 压缩 |
| 服务器推送 | 无 | 无 | 支持 | 支持 |
非持久 → 每对象 2 RTT(持久解决)→ 请求要串行等待(流水线解决)→ 响应要排队(HTTP/2 多路复用解决)→ TCP 按序交付导致跨流阻塞(HTTP/3 换 UDP 解决)。每一步只解决上一步剩下的那个阻塞,顺着这条链走就不必单独记每个版本的特性表。
HTTP/2 的三点改进逐条对应 1.1 的一个缺陷:响应可并行发回、复用 TCP 连接、二进制分帧 + 首部压缩(连续请求时首部里大量字段是重复的)。它向后兼容:HTTP/2 的客户向仍跑 1.1 的服务器发请求时服务器照样收得到,发回响应后客户改用 1.1 交互。HTTP/3 换 UDP 的理由是 HTTP/2 只消除了应用层的队头阻塞,底下仍是单条 TCP——TCP 要求按序交付,一个报文段丢了,后面所有已到达的数据都得在内核里等它重传;QUIC 自己实现流的独立可靠传输,一条流丢包不阻塞其他流。
六、代理服务器与条件 GET
代理服务器又称万维网高速缓存:把最近的一些请求和响应暂存在本地磁盘中,新请求若与暂存的相同就直接返回,不必再按 URL 去互联网访问。
代理服务器有时是服务器(接受浏览器请求时),有时是客户(向源点服务器发请求时)——角色由通信关系决定,与 网络应用模型 说的是同一件事。它的三项收益受益方不同:减少响应时间(用户)、减少机构出口链路流量(机构)、热门资源被缓存后源点请求量大幅下降(内容提供方)。规模化形态就是内容分发网络 CDN。
条件 GET 省掉的是实体主体的传输,省不掉那一个 RTT。 所以缓存验证只在"对象大、变化少"时划算——这也是静态资源要配长 TTL 而不是每次都验证的原因。
七、Cookie 与 Session
服务器在响应中用 Set-Cookie 首部设置 Cookie,客户端保存后在后续每个请求的 Cookie 首部里自动带上,服务器据此识别客户端。Session 则是服务器端保存的会话信息:服务器为每个会话分配一个 Session ID,通过 Cookie 发给客户端,客户端后续请求携带这个 ID,服务器据此找到对应的会话状态。
| Cookie | Session | |
|---|---|---|
| 存在哪 | 客户端 | 服务器端 |
| 随请求传输的内容 | 全部内容 | 只有 Session ID |
| 安全性 | 内容在客户端,可被查看/篡改 | 客户端只拿到一个 ID |
| 服务器开销 | 无 | 要为每个会话保存状态 |
本节小结
- 三句独立的话:HTTP 使用 TCP 连接 / 协议本身无连接 / 协议无状态;无状态是为了简化服务器、支撑高并发。请求与响应报文只在开始行不同,面向文本导致字段长度不定、靠 CRLF 划界。
- RTT 计数是本节的计算核心,地基是"请求捎带在握手第三个报文里,所以取一个文档 2 RTT"。四个公式为
、 、 、 ,恒定的那个 2 是取 HTML 的开销。持久省建连、并行省轮数、流水线省排队,三者优化维度不同,不能简单说谁快。 - 版本演进是一条追问链:非持久 → 持久 → 流水线 → HTTP/2 多路复用 → HTTP/3 换 UDP,每一步只解决上一步剩下的那个阻塞。代理服务器兼具客户与服务器双重身份;条件 GET 用
If-Modified-Since换 304,省的是实体主体不是往返。
教材出处
- 谢希仁《计算机网络》(第 8 版)印刷 p276,6.4.3 节:「协议 HTTP 定义了浏览器(即万维网客户进程)怎样向万维网服务器请求万维网文档,以及服务器怎样把文档传送给浏览器」「HTTP 是面向事务的(transaction-oriented)应用层协议」,脚注给出事务的定义「一系列的信息交换,而这一系列的信息交换是一个不可分割的整体,也就是说,要么所有的信息交换都完成,要么一次交换都不进行」。同页:「每个万维网网点都有一个服务器进程,它不断地监听 TCP 的端口 80。」
- 印刷 p277,6.4.3 节:无连接与无状态的两段原文——「HTTP 使用了面向连接的 TCP 作为运输层协议,保证了数据的可靠传输……但是,协议 HTTP 本身是无连接的。这就是说,虽然 HTTP 使用了 TCP 连接,但通信的双方在交换 HTTP 报文之前不需要先建立 HTTP 连接」;「协议 HTTP 是无状态的(stateless)……HTTP 的无状态特性简化了服务器的设计,使服务器更容易支持大量并发的 HTTP 请求」。同页还有本篇第四节的地基:「当建立 TCP 连接的三报文握手的前两个报文完成后(即经过了一个 RTT 时间后),万维网客户就把 HTTP 请求报文,作为建立 TCP 连接的三报文握手中的第三个报文的数据,发送给万维网服务器」,结论是「请求一个万维网文档所需的时间是该文档的传输时间(与文档大小成正比)加上两倍往返时间 RTT」。
- 印刷 p278,6.4.3 节:非持久的两项代价——「每一次链接下载都导致 2×RTT 的开销。另一种开销就是万维网客户和服务器每一次建立新的 TCP 连接都要分配缓存和变量」「好在浏览器都能够打开 5~10 个并行的 TCP 连接」。持久连接的定义与两种工作方式:「非流水线方式的特点,是客户在收到前一个响应后才能发出下一个请求……客户每访问一次对象都要用去一个往返时间 RTT。这比非持续连接要用去两倍 RTT 的开销,节省了建立 TCP 连接所需的一个 RTT 时间」;「流水线方式的特点,是客户在收到 HTTP 的响应报文之前就能够接着发送新的请求报文……使用流水线方式时,客户访问所有的对象只需花费一个 RTT 时间」。HTTP/2 的三点改进与队头阻塞描述也在本页:「但服务器发回响应时必须按先后顺序排队,逐个地发送给客户。有时遇到某个响应迟迟不能发回,那么排在后面的一些响应就必须等待很长的时间。HTTP/2 把服务器发回的响应变成可以并行地发回(使用同一个 TCP 连接)」。
- 印刷 p279,6.4.3 节 2. 代理服务器:「代理服务器(proxy server)是一种网络实体,它又称为万维网高速缓存(Web cache)。代理服务器把最近的一些请求和响应暂存在本地磁盘中。」以及代理访问互联网的五个步骤。
- 印刷 p280:「代理服务器有时是作为服务器(当接受浏览器的 HTTP 请求时),但有时却作为客户(当向互联网上的源点服务器发送 HTTP 请求时)」,以及 CDN 的定义。同页 3. HTTP 的报文结构:请求报文与响应报文「这两种报文格式的区别就是开始行不同」,「由于 HTTP 是面向文本的,因此在报文中的每一个字段都是一些 ASCII 码串,因而各个字段的长度都是不确定的」。
说明:折叠块里的 RTT、链路速率与文件大小均为自造数据;能与教材对账的是四个 RTT 计数公式的依据(非持久每对象 2 RTT、持久非流水线每对象 1 RTT、持久流水线所有对象共 1 RTT),已核对印刷 p277–p278 原文。HTTP/3 与 QUIC 一段在教材第 8 版正文中未展开,本篇只写到"换用 UDP 以避开 TCP 的按序交付约束"这一层,不展开 QUIC 细节。
相关知识
WWW 万维网|DNS 域名系统|TCP 连接管理|数据包的网络之旅|网络应用模型|FTP 与 DHCP