HTTP 协议学习笔记:从报文结构到抓包实践

一、HTTP 概述

本节先厘清 HTTP 的定位:它传输什么、处在网络模型的哪一层、用在哪些场景,以及它的通信模型。这些是后面理解报文、方法和缓存的基础。

1. 超文本传输协议

HTTP(HyperText Transfer Protocol,超文本传输协议)能传输的不仅仅是字符串,还有多媒体数据(二进制)。这也是它被称为"超文本"而不是"纯文本"的原因。

2. HTTP 是应用层协议

HTTP 在传输层主要依赖 TCP 协议,从 HTTP/3 开始切换到了 UDP。之所以切换,核心原因在于:HTTP/3 底层使用 QUIC 实现可靠传输,不再完全依赖 TCP 的重传机制;而 TCP 的拥塞控制在内核中实现,迭代慢,对性能影响明显。

3. HTTP 的应用场景

  • Web 开发中:网页前端和服务器后端之间的通信。
  • 移动端 App:和服务器后端之间的通信。
  • 分布式系统:服务器之间的调用。

4. 一问一答模型

HTTP 是典型的"一问一答"模型:客户端发送一个请求,服务器返回一个响应。

要点回顾:HTTP 是应用层协议,采用一问一答模型;HTTP/3 基于 QUIC 走 UDP,可靠传输由 QUIC 承担。

二、HTTP 请求和响应

本节看 HTTP 报文长什么样。HTTP 是纯文本协议,报文结构可以直接读懂,掌握请求报文和响应报文的四段式结构,是后续抓包分析的前提。

1. Request 请求报文

http 复制代码
GET https://www.bilibili.com/ HTTP/1.1
Host: www.bilibili.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: text/html,application/xhtml+xml
Cookie: buvid3=xxxx

(空行,说明 header 部分结束)
(请求正文,GET 通常省略)
  • 请求行:方法 + URL + 协议版本。
  • 请求报头:若干行键值对,每行一个,键和值之间用"冒号 + 空格"分隔,含义由 HTTP 协议规定。
  • 空行:说明 header 部分结束。
  • 请求正文:可能省略。

2. Response 响应报文

http 复制代码
HTTP/1.1 302 Found
Location: https://www.bilibili.com/index.html
Content-Type: text/html; charset=utf-8
Content-Length: 0

(空行,说明 header 部分结束)
(响应正文,可能省略)
  • 状态行:协议版本 + 状态码 + 状态描述。
  • 响应报头:同样是若干行键值对,含义由 HTTP 协议规定。
  • 空行:说明 header 部分结束。
  • 响应正文:可能省略。

要点回顾:请求报文是"请求行 + 报头 + 空行 + 正文",响应报文是"状态行 + 报头 + 空行 + 正文",空行是 header 与 body 的分界。

三、URL 网址

本节拆解 URL 的各个组成部分,并说明 query string 为什么需要编码。理解 URL 结构,才能明白后面 Host 头、路径和查询参数各自的作用。

URL(Uniform Resource Locator,统一资源定位符)的作用是表示一个网络上的位置。

以 https://mdec.nelreports.net/api/report?cat=mdocs 为例,逐段拆解:

  1. 协议 :https。URL 里还可以带登录信息(user:password@),但为了方便使用和防止写错,现在基本都是走登录页面认证,这种写法基本不用了。
  2. 端口号:没写端口号时,浏览器会根据协议类型给一个"知名端口号"作为默认值,HTTP 不写就是 80,HTTPS 不写就是 443。
  3. 服务器地址 :mdec.nelreports.net。这里写的是域名,直接写 IP 地址也是可以的。
  4. 带层次的文件路径 :/api/report。通过 IP 确定机器,通过端口确定程序;一个服务器程序可能会有很多资源,为了方便管理,就按照一定目录结构来组织(可能是真实的目录,也可能是通过代码逻辑构造的"虚拟目录结构")。
  5. 查询字符串 :?cat=mdocs。query string 也是键值对结构的数据,HTTP 的键值对是协议定义好的,这里的键值对是程序员自己定义的。
  6. 片段标识符 :#fragment,一般用来区分一个页面中的不同部分。注意:fragment 不会发送给服务器,它只在浏览器本地生效。

URL encode

query string 是程序员自定义的,如果包含了特殊符号呢?这时候就需要 encode 进行转码,URL 中很多特殊符号都是有特定含义的。

特殊符号需要转义,中文和空格等也通常会被浏览器编码:把要转义的内容每个字节拿出来,用十六进制表示,每个字节前面加上 %。

text 复制代码
https://example.com/search?q=C%2B%20%26%20%E4%B8%AD%E6%96%87%20100%25
解码后:https://example.com/search?q=C+%26+中文+100%

要点回顾:URL 由协议、端口、主机、路径、查询字符串和片段标识符组成;fragment 不发给服务器;特殊字符需要 URL 编码。

四、认识方法

本节介绍 HTTP 方法及其语义差异,重点澄清 GET 与 POST 的几个常见误区。方法选择直接影响接口语义、幂等性和缓存行为。

几种常见方法的对比如下:

方法 语义 是否有 body 幂等
GET 获取资源 通常没有 是
POST 提交数据 通常有 否(官方建议)
PUT 更新资源,和 POST 类似 通常有 是
DELETE 删除资源 通常没有 是

body 里面放的是程序员自定义的数据。

1. GET 请求

对于 GET 请求来说,通常情况下是没有 body 的。GET 是从服务器拿一个数据,到底是哪一个数据?靠两部分信息定位:

  1. URL 中的路径;
  2. URL 中的 query string。

2. POST 请求

对于 POST 请求来说,通常情况下都是有正文的。常见情况:登录和上传文件。

3. GET 和 POST 的区别

两者没有本质区别(能用 GET,也能换成 POST;能用 POST,也能用 GET),但使用习惯上有一定区别:

  • 语义不同:GET 的语义是获取数据,POST 的语义是提交数据。
  • 幂等性不同:GET 一般实现成"幂等",POST 没有"幂等"(官方建议)。但实际中 POST 常被人为设置为幂等,防止重复点击导致操作被执行多次。幂等的含义是:多次请求结果保持一致、不会产生额外副作用。
  • 传输数据量 :常说"GET 单次传输的数据量比较小,POST 比较大",这个说法不准确。真实原因是历史上浏览器和服务器对 URL 长度有上限,而现在 URL 也可以很长了,限制主要来自实现而非协议。
  • 能否传二进制 :常说"GET 只能传文本,POST 能传文本和二进制",这个说法也不准确。二进制数据可以通过 urlencode / Base64 转换为文本,再通过 GET 传输;只是 URL 中不能直接放二进制数据,而且 Base64 会让体积膨胀约 33%。

注意:GET 与 POST 的差异主要来自语义约定和实现限制,而不是协议层面的硬性规定。把"GET 不能传大数据/二进制"当成协议规则,是常见的误区。

4. PUT 和 DELETE

POST 和 PUT 类似,只是 PUT 具有幂等性,一般用于更新,通常把数据也放到 body 里面。DELETE 和 GET 类似,通常没有 body。

要点回顾:方法的核心差异在语义和幂等性;GET/POST 在数据量和二进制上的常见说法并不准确,限制主要来自实现。

五、Host

本节围绕 Host 头展开,并延伸到 Content-Length、Content-Type 和浏览器缓存。这些头部决定了请求被路由到哪个站点、body 如何解析,以及资源是否走缓存。

1. Host 表示服务器主机的地址和端口

Host 会把请求目标的主机和端口单独列出;URL 中的域名还需要经过一系列解析才能得到 IP 和端口。

Host 头独立于 URL 存在,意义在于:

  • 虚拟主机:一台服务器、一个 IP 上可以部署多个网站,靠 Host 区分访问的是哪个站点。
  • CDN / 缓存分流:代理和 CDN 节点根据 Host 决定回源到哪个源站、命中哪份缓存。

2. Content-Length 和 Content-Type

  • Content-Length:表示 body 中的数据长度,单位是字节。
  • Content-Type:表示 body 的数据格式。

这两个字段和 body 一起出现,有 body 通常就需要这两个字段,请求报头和响应报头中都可能出现。

3. Content-Type 常见取值

下表列出常见 Content-Type 及其在浏览器中的处理方式:

Content-Type 含义 浏览器处理方式
text/html HTML 文档 按 HTML 规则解析,渲染成网页骨架
text/css CSS 样式 按 CSS 规则解析,渲染成网页样式
application/javascript JavaScript 脚本 执行 JS 中的逻辑
application/json JSON 数据 浏览器一般不自主处理,由页面中的 JS 代码负责解析
image/jpeg JPEG 图片 直接渲染显示
image/png PNG 图片 直接渲染显示
text/plain 纯文本 按纯文本展示
application/x-www-form-urlencoded 表单编码,类似 query string 的格式 由后端框架解析成参数
multipart/form-data 多部分表单,常用于文件上传 由后端框架按分段解析,二进制原样传输

CSS/JS 开发阶段的换行空格是正常的,线上版本是被专门的工具进行了压缩,可以节省体积和网络传输带宽。

4. 强制刷新与浏览器缓存

强制刷新可以从网络上重新获取,不读缓存。HTML(网页结构)、CSS(网页样式)、JavaScript(网页行为)、图片等都是变动频率比较低的数据,浏览器通常会把上述数据保存到本机硬盘上作为"缓存",下次直接从硬盘读取。

协商缓存里最常见的是 304 Not Modified :浏览器带着 If-Modified-Since 或 If-None-Match 去问服务器"资源变了没",没变就返回 304,浏览器继续用本地缓存,省掉正文传输。

要点回顾:Host 决定请求落到哪个站点;Content-Length 和 Content-Type 描述 body;304 是协商缓存命中的典型状态码。

六、User-Agent(简称 UA)

本节说明 UA 的组成和用途。UA 是服务端识别客户端环境、做兼容和分流的重要依据。

http 复制代码
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
  • Mozilla/5.0:历史遗留的兼容标识,几乎所有浏览器都这么写。
  • Windows NT 10.0:操作系统的版本。
  • Win64:64 位操作系统。
  • 后面是浏览器内核和版本信息。

UA 主要描述浏览器和操作系统的版本信息,常用来区分手机和 PC。

要点回顾:UA 描述客户端环境,常用于兼容判断和端类型分流。

七、Referer 和 Location

本节介绍两个与跳转相关的头部:Referer 记录来源页面,Location 指示重定向目标。理解它们有助于排查来源统计和重定向问题。

1. Referer

表示这个页面是从哪个页面跳转过来的。不是所有请求都有 Referer,以下情况通常没有:

  • 直接在地址栏输入 URL;
  • 直接访问收藏夹;
  • 从 HTTPS 页面跳到 HTTP 页面(浏览器默认不发送);
  • 页面设置了 Referrer-Policy: no-referrer;
  • 请求本身不是由页面跳转触发的(如部分直接发起的请求)。

2. Location

表示服务器告诉浏览器要去哪个网页,常用于重定向,一般和 3xx 状态码一起出现。

要点回顾:Referer 不一定存在,不能假设它总是可用;Location 与 3xx 状态码配合完成重定向。

八、Cookie

本节讲 Cookie 的作用、下发与回传方向、存储方式和典型用途,并厘清 Cookie 与 Session 的关系。这是理解登录态的基础。

1. 作用

Cookie 是浏览器侧的本地存储机制。安装的应用程序一般能访问硬盘,浏览器一般不能访问,这时候 Cookie 允许网页按照"键值对"的格式组织一个字符串,浏览器会把这个字符串保存到本地硬盘中。

2. 从哪里来、到哪里去

  • 下发方向 :服务器通过响应头 Set-Cookie 把要存在浏览器本地的 cookie 值传递过去。
  • 回传方向 :本地存储以后,后续访问同一个域名的网站,浏览器会把 cookie 的内容通过请求头 Cookie 传输给服务器。

3. 怎么存

按照字符串(键值对)方式来存储,根据域名维度划分,每个域名下有自己的 cookie,不同域名之间互相隔离。

4. 用途

用途有很多,但最常见、最核心的就是保存用户的登录状态。

服务端维护了一个 HashMap,key = sessionId,value = session 对象。Cookie 和 Session 不是强相关:登录也可以使用其他技术搭配 Session,其他场景下 Cookie 也可以完成其他功能。

要点回顾:Set-Cookie 下发、Cookie 回传;Cookie 按域名隔离;Cookie 与 Session 并非强绑定。

九、常见的状态码

本节汇总常见状态码,并重点区分容易混淆的 301/302 缓存差异和 502/504 的故障含义。状态码是排查线上问题最直接的线索。

常见状态码及其说明如下:

状态码 含义 说明
200 OK 成功
301 Moved Permanently 永久重定向,可被缓存
302 Found 临时重定向,默认不缓存
304 Not Modified 协商缓存命中,继续用本地缓存
403 Forbidden 拒绝访问
404 Not Found 访问的资源在服务器上不存在
405 Method Not Allowed 服务器不支持这个方法
500 Internal Server Error 服务器内部错误
502 Bad Gateway 网关/代理从上游收到无效响应
504 Gateway Timeout 网关/代理等待上游响应超时

4xx 都属于客户端错误,5xx 都表示服务器错误。502 和 504 的区别:502 是上游返回了坏响应,504 是上游没有在超时时间内响应。

301 和 302 的缓存差异:301 会被浏览器长期缓存,下次直接跳新地址;302 默认不缓存,每次仍会请求原地址。

注意:502 与 504 都常出现在网关/代理层,但含义不同------前者是上游响应无效,后者是上游超时未响应,排查方向也不同。

要点回顾:4xx 是客户端问题,5xx 是服务端问题;301 可缓存、302 默认不缓存;502 看上游响应,504 看上游超时。

十、构造 HTTP 请求

本节介绍构造 HTTP 请求的几种方式,并给出 Java 11+ HttpClient 的示例。理解如何手工构造请求,有助于验证协议细节和排查问题。

  1. 通过 form 表单构造 HTTP 请求:纯 HTML 的方案。
  2. 通过 AJAX 构造 HTTP 请求:通过 JavaScript 构造的 HTTP 请求。
  3. 通过 Java Socket 构造 HTTP 请求。

下面是 Java 11+ 的 HttpClient 示例:

java 复制代码
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Path;

public class MyHttpClient {
    public static void main(String[] args) throws IOException, InterruptedException {
        // 创建一个 http 实例,用工厂方法
        HttpClient httpClient = HttpClient.newHttpClient();

        // 创建一个 HttpRequest,表示一个 http 请求,链式结构
        HttpRequest request = HttpRequest.newBuilder()
                // 目标地址
                .uri(URI.create("https://www.bilibili.com"))
                .GET()
                // 可以放 Cookie、Content-Length 之类的
                .header("User-Agent", "xxxxxx")
                .build();

        // 发送给服务器 + 接收响应
        // BodyHandlers 负责把 body 里面的内容转换为字符串
        // send 会产生阻塞,直到服务器响应
        HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode());
        System.out.println(response.uri());
        System.out.println(response.body());

        // 大文件不要用 ofString,直接落盘,避免整个文件读进内存
        HttpResponse<Path> fileResponse = httpClient.send(request, HttpResponse.BodyHandlers.ofFile(Path.of("download.html")));
        System.out.println(fileResponse.body());
    }
}

也有不写代码的方案,使用专门的构造 HTTP 请求的工具:

  1. Postman
  2. Apifox

要点回顾 :构造请求可以用表单、AJAX、Socket 或 HttpClient;大文件响应应使用 ofFile 落盘,避免整体读入内存。

十一、Fiddler 的用法

本节介绍抓包工具的本质和使用方法。抓包是验证协议细节、定位前后端问题最直接的手段。

学习协议报文时,少不了抓包工具,这里使用的是 Fiddler。

1. 抓包工具的本质

通过软件读取网卡的内容,从而看到请求和响应的数据明细,本质上是一种"代理"。代理也分为"正向代理"(代替客户端干活)和"反向代理"(代替服务器干活)。

2. 用抓包定位 bug

如果要找一个功能的 bug,先抓包,通过网络传输的数据初步判定是前端出 bug 还是后端:

  • 请求本身就不对(前端 bug);
  • 请求正确,响应错误(后端 bug);
  • 请求正确,响应正确(前端 bug)。

例如:网页中不能显示预期数据,就看请求与响应的详细情况。

3. 第一次使用需要设置

把相关选项全勾选上,弹出"是否要信任根证书"也要选 YES,根证书是 HTTPS 抓包的前提。

4. 配置完成以后要重启才会生效

Raw 会展示出 HTTP 原始的数据格式(如果想自己构造一个 HTTP 请求,就照着上述格式拼装一个字符串写到 TCP socket 里面),看到乱码说明响应内容被压缩了。

要点回顾:抓包工具本质是代理;通过请求与响应是否正确,可以快速划分前后端问题;HTTPS 抓包依赖信任根证书。

相关推荐
沫璃染墨2 小时前
从零入门计算机网络系列(十二):HTTP长连接与会话管理详解——从Connection到Cookie与Session
linux·服务器·c++·tcp/ip·ubuntu·http·交互
Black蜡笔小新7 小时前
上云网关/端口穿透/设备上云EasyNTS:无需公网IP、也不改防火墙,设备怎么从内网跑到外网去
网络·网络协议·tcp/ip
小贾要学习12 小时前
【C++后端开发项目】Reactor 模型 / C++ 仿写 muduo HTTP 服务器:让底层网络库真正跑起网页
linux·服务器·c++·http·网络编程·muduo·reactor模型
g5zhu589612 小时前
没有公网ip的情况下,本地能ping服务器服务器不能ping本地,让服务器能访问本地某端口的简单方法
服务器·网络协议·tcp/ip
guizhoumen13 小时前
net core下WebSocket请求总是断开的解决方案
网络·websocket·网络协议
小园子的小菜14 小时前
Netty 核心组件 Channel、ChannelHandler 与简易 RPC 架构实战详解
网络协议·rpc·架构
一条泥憨鱼1 天前
【计算机网络】短连接、长连接与连接池:从 TCP 握手到池化复用的完整链路
网络·数据库·网络协议·tcp/ip·计算机网络
cyf311 天前
第 05 篇:IP 协议—— 网络层的“快递单号与物流追踪“
网络协议·tcp/ip·路由表·traceroute·ip协议·ping·ip分片