3. HTTP 协议基础回顾:请求与响应模型、URL 结构、HTTP 版本与无状态特性

在开始抓包之前,我建议咱们先花点时间把 HTTP 协议的基础再过一遍。你可能会觉得这有点基础,但说实话,很多网络问题排查到最后,都是卡在这些基础概念上。我自己带团队时发现,能快速定位问题的人,往往对协议的理解最深。

3.1 HTTP 请求与响应模型:一问一答的对话

HTTP 本质上是一个「请求-响应」模型。说白了,就是客户端问一句,服务器答一句。没有客户端主动问,服务器永远不会主动说话。

我经常把 HTTP 比作打电话:你拨号(发起请求),对方接听(建立连接),你说“帮我查一下今天的天气”(发送请求),对方回答“今天是晴天”(返回响应)。挂断电话后,刚才的对话就结束了。下次你再打过去,对方不会记得你是谁——这就是 HTTP 的无状态特性,我们后面会细说。

核心流程:

  1. 客户端(浏览器、App 等)发起 TCP 连接(三次握手)
  2. 客户端发送 HTTP 请求报文
  3. 服务器处理请求并返回 HTTP 响应报文
  4. 连接关闭(或保持复用,取决于版本)

在 Wireshark 里,你看到的 HTTP 包就是这种一问一答的完整记录。我排查过一个线上问题:用户反馈页面加载慢,抓包后发现客户端发了请求,但服务器隔了 5 秒才响应。一看报文,原来是请求头里带了个巨大的 Cookie,服务器解析花了太长时间。

3.2 URL 结构:拆解一个网址

URL(统一资源定位符)就是你在浏览器地址栏里看到的那串东西。很多人觉得它很简单,但抓包时你经常需要拆解 URL 来分析问题。

一个完整的 URL 长这样:

https://www.example.com:8080/path/to/resource?name=alice&age=25#section1

拆开来看,每个部分都有它的作用:

组成部分 示例 说明
协议(Scheme) https 告诉客户端用什么协议通信,常见的有 http、https、ftp
主机(Host) www.example.com 服务器的域名或 IP 地址
端口(Port) 8080 HTTP 默认 80,HTTPS 默认 443,非默认端口必须显式写出
路径(Path) /path/to/resource 服务器上资源的定位路径
查询参数(Query) name=alice&age=25 以 ? 开头,键值对形式,用 & 分隔
片段(Fragment) #section1 页面内的锚点,不会发送到服务器

抓包小技巧: 在 Wireshark 的过滤栏输入 http.request.uri 可以快速筛选出包含特定路径或参数的请求。我曾经用这个技巧在几千个包中一秒定位到某个 API 调用。

这里有个容易踩的坑:片段(# 后面的部分)不会出现在 HTTP 请求中。我见过有新手在抓包时发现请求里没有 #section1,以为包丢了,其实它本来就不会发出去。片段只在浏览器本地使用。

3.3 HTTP 版本演进:1.0、1.1、2.0

HTTP 协议经历了几个版本的迭代,每个版本解决了一些痛点,也带来了新的特性。抓包时你会在请求行或响应行里看到版本号,比如 HTTP/1.1 或 HTTP/2。

HTTP/1.0:短连接,用完就扔

HTTP/1.0 时代,每次请求都要新建一个 TCP 连接。你想想看,一个网页有 100 张图片,浏览器就得建立 100 次 TCP 连接,每次都要三次握手、四次挥手。这效率,说实话低得离谱。

HTTP/1.1:长连接与管道化

HTTP/1.1 引入了持久连接(Keep-Alive),默认情况下连接不会立即关闭,可以复用。还支持管道化(Pipelining),允许客户端在收到响应前连续发送多个请求。

但管道化有个问题:响应必须按请求的顺序返回。如果第一个请求处理得很慢,后面的请求即使处理完了也得等着。这就是所谓的「队头阻塞」。我在项目中遇到过这种情况:一个慢查询拖垮了整个页面的加载。

HTTP/2:多路复用与二进制分帧

HTTP/2 彻底解决了队头阻塞的问题。它引入了二进制分帧层,将请求和响应拆分成更小的帧,多个帧可以在同一个连接上交错传输。说白了,就是不再需要排队了,谁先处理完谁先走。

另外,HTTP/2 还支持服务器推送(Server Push),服务器可以主动把客户端可能需要的内容推过去。不过这个特性在实际项目中用得不多,我个人的经验是它反而可能浪费带宽。

特性 HTTP/1.0 HTTP/1.1 HTTP/2
连接方式 短连接(每次新建) 持久连接(默认 Keep-Alive) 多路复用(单一连接)
队头阻塞 无(但连接开销大) 有(响应必须按序返回) 无(帧交错传输)
数据传输 文本 文本 二进制分帧
头部压缩 无 无 HPACK 压缩
服务器推送 不支持 不支持 支持

注意: 虽然 HTTP/2 解决了应用层的队头阻塞,但 TCP 层的队头阻塞依然存在。如果某个 TCP 包丢失了,后续所有 HTTP/2 的帧都得等重传。这也是为什么 HTTP/3 要改用 QUIC(基于 UDP)的原因。

3.4 无状态特性:服务器不记得你

HTTP 是无状态的。什么意思?就是服务器处理完你的请求后,就把你忘了。下次你再发请求,它不认识你。

你可能会问:「那登录状态是怎么保持的?」嗯,这就是 Cookie 和 Session 的用武之地了。服务器通过 Set-Cookie 响应头给客户端发一个标识,客户端下次请求时带上这个 Cookie,服务器就能认出你来。

我在排查一个登录失效的问题时,抓包发现客户端的 Cookie 过期了,但服务器还在用旧的 Session ID 去查,结果查不到。这就是典型的无状态特性带来的问题——状态必须由客户端和服务器共同维护。

无状态的好处也很明显:服务器不需要保存每个客户端的状态,可以轻松地横向扩展。你想想看,如果服务器要记住每个用户的上一次操作,那加机器就麻烦了,因为状态不共享。

3.5 本章知识体系

下面这张图把本章的核心知识点串起来了,你可以对照着回顾一下:

HTTP 协议基础核心知识体系 HTTP 协议基础核心知识体系 HTTP 协议 请求-响应模型 URL 结构 HTTP 版本 无状态特性 客户端发起请求 服务器返回响应 协议 + 主机 + 端口 路径 + 查询 + 片段 1.0 短连接 1.1 长连接 + 管道化 2.0 多路复用 + 二进制 服务器不保存状态 通过 Cookie 维持会话 理解这些基础,抓包时你才能看懂每一行报文在说什么

好了,HTTP 的基础就回顾到这里。这些概念在后续的抓包实战中会反复用到,尤其是当你看到请求行、响应状态码和各种头部字段时,你会感谢自己现在花时间理解了它们。