HTTP协议帧格式

只看 HTTP 层,可以先记住一句话:

HTTP 层没有像 UART 那种固定"帧头/帧尾/校验"的二进制帧格式。

HTTP/1.1 主要是 文本协议 ,格式是:
起始行 + 头部字段 + 空行 + 可选正文 Body

天气案例里,ESP32 发的是 HTTP Request 请求报文 ,服务器回的是 HTTP Response 响应报文


1. HTTP 请求报文格式

HTTP 请求格式:

text 复制代码
请求行\r\n
请求头1\r\n
请求头2\r\n
请求头3\r\n
...\r\n
\r\n
请求体 Body

也就是:

text 复制代码
┌──────────────────────────────┐
│ Request Line 请求行           │
├──────────────────────────────┤
│ Headers 请求头                │
├──────────────────────────────┤
│ 空行 \r\n                     │
├──────────────────────────────┤
│ Body 请求体,可选              │
└──────────────────────────────┘

天气 GET 请求通常没有 Body。


2. 天气案例 HTTP GET 请求

比如 ESP32 请求大阪天气:

http 复制代码
GET /data/2.5/weather?q=Osaka&appid=YOUR_API_KEY&units=metric&lang=zh_cn HTTP/1.1
Host: api.openweathermap.org
User-Agent: esp32
Connection: close

注意:最后必须有一个空行。

在 C 字符串里要写成:

c 复制代码
const char *http_request =
    "GET /data/2.5/weather?q=Osaka&appid=YOUR_API_KEY&units=metric&lang=zh_cn HTTP/1.1\r\n"
    "Host: api.openweathermap.org\r\n"
    "User-Agent: esp32\r\n"
    "Connection: close\r\n"
    "\r\n";

3. 请求行格式

请求行格式:

text 复制代码
Method SP Request-URI SP HTTP-Version CRLF

对应天气请求:

http 复制代码
GET /data/2.5/weather?q=Osaka&appid=YOUR_API_KEY&units=metric&lang=zh_cn HTTP/1.1

拆开看:

text 复制代码
GET
/data/2.5/weather?q=Osaka&appid=YOUR_API_KEY&units=metric&lang=zh_cn
HTTP/1.1

含义:

字段 含义
GET 请求方法,表示获取数据
/data/2.5/weather?... 请求资源路径和参数
HTTP/1.1 HTTP 协议版本

其中 SP 就是空格,CRLF 就是 \r\n


4. 请求头格式

每个请求头格式都是:

text 复制代码
Header-Name: Header-Value\r\n

比如:

http 复制代码
Host: api.openweathermap.org
User-Agent: esp32
Connection: close

这些头部字段的作用:

请求头 作用
Host 告诉服务器你访问的是哪个域名
User-Agent 告诉服务器客户端是谁
Connection: close 请求完成后关闭连接

HTTP/1.1 里 Host 基本是必须的。


5. 空行很重要

请求头结束后,要有一个空行:

text 复制代码
\r\n

完整请求末尾实际是:

text 复制代码
Connection: close\r\n
\r\n

这个空行的意思是:

请求头结束了,后面如果还有内容,就是 Body。

天气 GET 请求没有 Body,所以空行后面就没东西了。


6. HTTP 响应报文格式

服务器返回格式:

text 复制代码
状态行\r\n
响应头1\r\n
响应头2\r\n
响应头3\r\n
...\r\n
\r\n
响应体 Body

结构是:

text 复制代码
┌──────────────────────────────┐
│ Status Line 状态行            │
├──────────────────────────────┤
│ Headers 响应头                │
├──────────────────────────────┤
│ 空行 \r\n                     │
├──────────────────────────────┤
│ Body 响应体                   │
└──────────────────────────────┘

7. 天气案例 HTTP 响应

服务器可能返回:

http 复制代码
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 168
Connection: close

{
  "weather": [
    {
      "main": "Clouds",
      "description": "多云"
    }
  ],
  "main": {
    "temp": 23.5,
    "humidity": 60
  },
  "name": "Osaka"
}

8. 状态行格式

状态行格式:

text 复制代码
HTTP-Version SP Status-Code SP Reason-Phrase CRLF

比如:

http 复制代码
HTTP/1.1 200 OK

拆开:

字段 含义
HTTP/1.1 HTTP 协议版本
200 状态码
OK 状态描述

常见状态码:

状态码 含义
200 OK 请求成功
301/302 重定向
400 Bad Request 请求格式或参数错误
401 Unauthorized API Key 错误或无权限
403 Forbidden 禁止访问
404 Not Found API 路径错误
500 Internal Server Error 服务器内部错误

9. 响应头格式

响应头也是:

text 复制代码
Header-Name: Header-Value\r\n

比如:

http 复制代码
Content-Type: application/json; charset=utf-8
Content-Length: 168
Connection: close

含义:

响应头 含义
Content-Type Body 的数据类型,这里是 JSON
Content-Length Body 的长度,单位字节
Connection 连接控制

10. 响应体 Body

空行后面的内容就是 Body:

json 复制代码
{
  "weather": [
    {
      "main": "Clouds",
      "description": "多云"
    }
  ],
  "main": {
    "temp": 23.5,
    "humidity": 60
  },
  "name": "Osaka"
}

ESP32 真正要解析的是这一部分。

也就是:

text 复制代码
HTTP 响应头:告诉你返回了什么
HTTP 响应体:真正的天气数据

11. HTTP GET 请求完整字节格式

天气 GET 请求在 HTTP 层实际就是一串 ASCII 字符:

text 复制代码
GET /data/2.5/weather?q=Osaka&appid=YOUR_API_KEY&units=metric&lang=zh_cn HTTP/1.1\r\n
Host: api.openweathermap.org\r\n
User-Agent: esp32\r\n
Connection: close\r\n
\r\n

如果把换行展开看:

text 复制代码
GET /data/2.5/weather?q=Osaka&appid=YOUR_API_KEY&units=metric&lang=zh_cn HTTP/1.1[CRLF]
Host: api.openweathermap.org[CRLF]
User-Agent: esp32[CRLF]
Connection: close[CRLF]
[CRLF]

[CRLF] 就是:

text 复制代码
\r\n

对应十六进制:

text 复制代码
0D 0A

所以 HTTP 头部每行结尾都是:

text 复制代码
0D 0A

头部结束是两个连续的 CRLF:

text 复制代码
0D 0A 0D 0A

12. HTTP 响应完整格式

服务器响应在 HTTP 层也是文本加 Body:

text 复制代码
HTTP/1.1 200 OK\r\n
Content-Type: application/json; charset=utf-8\r\n
Content-Length: 168\r\n
Connection: close\r\n
\r\n
{"weather":[{"main":"Clouds","description":"多云"}],"main":{"temp":23.5,"humidity":60},"name":"Osaka"}

可以理解为:

text 复制代码
响应头部分:
HTTP/1.1 200 OK\r\n
Content-Type: application/json; charset=utf-8\r\n
Content-Length: 168\r\n
Connection: close\r\n
\r\n

响应体部分:
{"weather":[...],"main":{...},"name":"Osaka"}

13. GET 请求为什么没有 Body?

天气查询是获取数据,一般参数都放在 URL 后面:

text 复制代码
/data/2.5/weather?q=Osaka&appid=YOUR_API_KEY&units=metric

所以 GET 请求通常没有 Body。

text 复制代码
GET 请求:
参数在 URL 里
Body 一般为空

14. 如果是 POST,HTTP 层格式会不一样

比如 ESP32 上传传感器数据:

http 复制代码
POST /api/weather/upload HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 31
Connection: close

{"temp":23.5,"humidity":60}

POST 报文结构:

text 复制代码
请求行
请求头
空行
请求体

其中请求体是:

json 复制代码
{"temp":23.5,"humidity":60}

POST 必须告诉服务器 Body 多长,所以经常有:

http 复制代码
Content-Length: 31

15. Content-Length 是干啥的?

比如响应:

http 复制代码
Content-Length: 168

意思是:

空行后面的 Body 有 168 字节。

ESP32 接收时,可以根据这个判断 Body 有没有收完整。

如果没有 Content-Length,也可能用:

http 复制代码
Transfer-Encoding: chunked

16. Chunked 格式是什么?

有些服务器不知道一次返回多大,就会分块返回:

http 复制代码
HTTP/1.1 200 OK
Content-Type: application/json
Transfer-Encoding: chunked

A
0123456789
5
abcde
0

格式是:

text 复制代码
块大小,十六进制
块内容
块大小,十六进制
块内容
0
结束

对新手来说你先知道:

text 复制代码
Content-Length:提前告诉你 Body 总长度
chunked:一块一块发,最后用 0 表示结束

如果你用 esp_http_client,很多细节它会帮你处理。

如果你自己 socket 手写 HTTP,就要注意这个坑。


17. 只看 HTTP 层,请求和响应总结

HTTP 请求

text 复制代码
┌────────────────────────────────────────────┐
│ 请求行                                      │
│ GET /data/2.5/weather?... HTTP/1.1          │
├────────────────────────────────────────────┤
│ 请求头                                      │
│ Host: api.openweathermap.org                │
│ User-Agent: esp32                           │
│ Connection: close                           │
├────────────────────────────────────────────┤
│ 空行                                        │
│ \r\n                                        │
├────────────────────────────────────────────┤
│ 请求体                                      │
│ GET 一般为空                                │
└────────────────────────────────────────────┘

HTTP 响应

text 复制代码
┌────────────────────────────────────────────┐
│ 状态行                                      │
│ HTTP/1.1 200 OK                             │
├────────────────────────────────────────────┤
│ 响应头                                      │
│ Content-Type: application/json              │
│ Content-Length: xxx                         │
│ Connection: close                           │
├────────────────────────────────────────────┤
│ 空行                                        │
│ \r\n                                        │
├────────────────────────────────────────────┤
│ 响应体                                      │
│ {"main":{"temp":23.5},"weather":[...]}      │
└────────────────────────────────────────────┘

18. 你作为嵌入式开发要重点记住

HTTP 层最重要的是这几个:

text 复制代码
1. 请求行 / 状态行
2. Header 头部
3. 空行 \r\n
4. Body
5. GET 通常没有 Body
6. POST 通常有 Body
7. Body 长度由 Content-Length 或 chunked 决定
8. 天气 JSON 在响应 Body 里面

一句话总结:

text 复制代码
HTTP 请求 = 你问服务器要什么
HTTP 响应 = 服务器告诉你结果,并把数据放在 Body 里

天气案例中:

text 复制代码
ESP32 HTTP Request:
GET /data/2.5/weather?... HTTP/1.1

服务器 HTTP Response:
HTTP/1.1 200 OK
...
天气 JSON 数据



更详细的底层原理

天气案例里,HTTP 请求和应答不是直接裸奔在 Wi-Fi 上的,它是一层一层封装的。

你可以这样理解:

text 复制代码
天气 JSON
  ↑
HTTP 协议
  ↑
TCP 协议
  ↑
IP 协议
  ↑
Wi-Fi / 以太网
  ↑
无线电波 / 网线

对 ESP32 来说,获取天气时真正发出去的数据大概是:

text 复制代码
[Wi-Fi帧头] [IP头] [TCP头] [HTTP请求内容]

服务器返回时也是:

text 复制代码
[Wi-Fi帧头] [IP头] [TCP头] [HTTP响应内容]

1. 天气 HTTP 请求完整流程

假设 ESP32 要访问:

text 复制代码
http://api.openweathermap.org/data/2.5/weather?q=Osaka&appid=API_KEY&units=metric&lang=zh_cn

完整过程是:

text 复制代码
ESP32 连接 Wi-Fi
  ↓
DNS 解析 api.openweathermap.org 得到服务器 IP
  ↓
TCP 三次握手连接服务器 80 端口
  ↓
发送 HTTP GET 请求
  ↓
服务器返回 HTTP 响应
  ↓
ESP32 解析 JSON
  ↓
TCP 连接关闭

2. 协议栈分层格式

从上到下看:

text 复制代码
应用层:HTTP
传输层:TCP
网络层:IP
链路层:Wi-Fi / Ethernet
物理层:无线信号

天气请求封装后大概是这样:

text 复制代码
┌────────────────────────────────────┐
│ Wi-Fi / Ethernet 帧头              │ ← 局域网传输用
├────────────────────────────────────┤
│ IP Header                          │ ← 源 IP、目标 IP
├────────────────────────────────────┤
│ TCP Header                         │ ← 源端口、目标端口、序号
├────────────────────────────────────┤
│ HTTP Request                       │ ← GET 天气请求
└────────────────────────────────────┘

HTTP 响应也是类似:

text 复制代码
┌────────────────────────────────────┐
│ Wi-Fi / Ethernet 帧头              │
├────────────────────────────────────┤
│ IP Header                          │
├────────────────────────────────────┤
│ TCP Header                         │
├────────────────────────────────────┤
│ HTTP Response                      │ ← 天气 JSON 数据
└────────────────────────────────────┘

3. HTTP 请求报文格式

HTTP 请求本身长这样:

http 复制代码
GET /data/2.5/weather?q=Osaka&appid=API_KEY&units=metric&lang=zh_cn HTTP/1.1
Host: api.openweathermap.org
User-Agent: esp32
Connection: close

注意最后有一个空行。

标准格式是:

text 复制代码
请求行
请求头1
请求头2
请求头3
空行
请求体

对于 GET 请求,一般没有请求体。

所以天气 HTTP GET 请求可以拆成:

text 复制代码
请求行:
GET /data/2.5/weather?q=Osaka&appid=API_KEY&units=metric&lang=zh_cn HTTP/1.1

请求头:
Host: api.openweathermap.org
User-Agent: esp32
Connection: close

空行:
\r\n

请求体:
GET 请求通常为空

4. HTTP 请求行格式

这一行:

http 复制代码
GET /data/2.5/weather?q=Osaka&appid=API_KEY&units=metric&lang=zh_cn HTTP/1.1

拆开是:

text 复制代码
GET                         请求方法
/data/2.5/weather?...       请求路径和参数
HTTP/1.1                    HTTP 协议版本

也就是:

text 复制代码
方法 + 空格 + URL路径 + 空格 + HTTP版本 + \r\n

真正格式:

text 复制代码
GET /xxx/xxx?参数1=值1&参数2=值2 HTTP/1.1\r\n

5. HTTP 请求头格式

请求头每一行格式是:

text 复制代码
字段名: 字段值\r\n

比如:

http 复制代码
Host: api.openweathermap.org
User-Agent: esp32
Connection: close

对应含义:

请求头 含义
Host 要访问哪个网站/服务器
User-Agent 客户端身份,这里是 ESP32
Connection: close 服务器返回后关闭连接

HTTP 请求头结束后,必须有一个空行:

text 复制代码
\r\n

所以完整 HTTP 请求在 C 语言里一般写成这样:

c 复制代码
const char *request =
    "GET /data/2.5/weather?q=Osaka&appid=API_KEY&units=metric&lang=zh_cn HTTP/1.1\r\n"
    "Host: api.openweathermap.org\r\n"
    "User-Agent: esp32\r\n"
    "Connection: close\r\n"
    "\r\n";

\r\n 是 HTTP 协议要求的换行符。


6. 这个 HTTP 请求在 TCP 里长什么样?

HTTP 本身只是 TCP 负载。

TCP 包大概这样:

text 复制代码
┌────────────────────────────────────┐
│ TCP Header                         │
│ - 源端口:随机端口,例如 54321     │
│ - 目标端口:80                    │
│ - 序号 seq                        │
│ - 确认号 ack                      │
│ - 标志位 PSH ACK                  │
├────────────────────────────────────┤
│ TCP Payload                        │
│ GET /data/2.5/weather?... HTTP/1.1 │
│ Host: api.openweathermap.org       │
│ User-Agent: esp32                  │
│ Connection: close                  │
│                                    │
└────────────────────────────────────┘

也就是说:

text 复制代码
TCP 不知道你这是天气请求。
TCP 只知道:我要把这一串字节可靠地送到服务器 80 端口。

HTTP 的内容在 TCP 的数据区里。


7. 这个 TCP 包在 IP 里长什么样?

IP 包大概这样:

text 复制代码
┌────────────────────────────────────┐
│ IP Header                          │
│ - 源 IP:ESP32 的 IP,比如 192.168.1.20
│ - 目标 IP:天气服务器 IP
│ - 协议号:6,表示 TCP
│ - TTL
│ - 总长度
├────────────────────────────────────┤
│ TCP Segment                        │
│ ┌────────────────────────────────┐ │
│ │ TCP Header                     │ │
│ ├────────────────────────────────┤ │
│ │ HTTP GET 请求                  │ │
│ └────────────────────────────────┘ │
└────────────────────────────────────┘

IP 层关心的是:

text 复制代码
从哪个 IP 来?
要发到哪个 IP 去?
上层协议是什么?

对 HTTP 来说,上层协议是 TCP,所以 IP Header 里:

text 复制代码
Protocol = 6

8. 这个 IP 包在 Wi-Fi 里长什么样?

ESP32 用 Wi-Fi 发出去时,还会再加 Wi-Fi 帧头。

简化后:

text 复制代码
┌────────────────────────────────────┐
│ Wi-Fi MAC Header                   │
│ - 源 MAC:ESP32 的 MAC             │
│ - 目标 MAC:路由器/AP 的 MAC       │
├────────────────────────────────────┤
│ LLC/SNAP                           │
├────────────────────────────────────┤
│ IP Packet                          │
│ ┌────────────────────────────────┐ │
│ │ IP Header                      │ │
│ ├────────────────────────────────┤ │
│ │ TCP Header                     │ │
│ ├────────────────────────────────┤ │
│ │ HTTP GET 请求                  │ │
│ └────────────────────────────────┘ │
├────────────────────────────────────┤
│ FCS 校验                           │
└────────────────────────────────────┘

你可以先不用纠结 Wi-Fi 帧头细节。

嵌入式软件开发时重点先掌握:

text 复制代码
HTTP 在 TCP 里面
TCP 在 IP 里面
IP 在 Wi-Fi / Ethernet 里面

9. 服务器返回的 HTTP 响应格式

服务器返回大概是:

http 复制代码
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 180
Connection: close

{
  "weather": [
    {
      "main": "Clouds",
      "description": "多云"
    }
  ],
  "main": {
    "temp": 23.5,
    "humidity": 60
  },
  "name": "Osaka"
}

HTTP 响应格式是:

text 复制代码
状态行
响应头1
响应头2
响应头3
空行
响应体

10. HTTP 响应状态行

这一行:

http 复制代码
HTTP/1.1 200 OK

拆开是:

text 复制代码
HTTP/1.1    协议版本
200         状态码
OK          状态说明

常见状态码:

状态码 含义
200 OK 请求成功
301/302 重定向
400 请求参数错误
401 API Key 错误或无权限
404 路径不存在
500 服务器内部错误

11. HTTP 响应头

比如:

http 复制代码
Content-Type: application/json; charset=utf-8
Content-Length: 180
Connection: close

含义是:

响应头 含义
Content-Type 返回的数据类型,这里是 JSON
Content-Length 响应体长度
Connection 连接是否关闭

12. HTTP 响应体

空行后面的才是真正的天气数据:

json 复制代码
{
  "weather": [
    {
      "main": "Clouds",
      "description": "多云"
    }
  ],
  "main": {
    "temp": 23.5,
    "humidity": 60
  },
  "name": "Osaka"
}

ESP32 最后用 cJSON 解析的是这部分。

你关心的是:

text 复制代码
main.temp                 温度
main.humidity             湿度
weather[0].description    天气描述
name                      城市名

13. 响应在协议栈里怎么封装?

服务器返回时,方向反过来。

text 复制代码
服务器应用层生成 HTTP 响应
  ↓
TCP 封装
  ↓
IP 封装
  ↓
链路层封装
  ↓
互联网传输
  ↓
路由器
  ↓
Wi-Fi 发给 ESP32

ESP32 收到后逐层拆包:

text 复制代码
Wi-Fi 驱动收到无线帧
  ↓
取出 IP 包
  ↓
IP 层发现这是发给自己的
  ↓
取出 TCP 段
  ↓
TCP 层重组数据流
  ↓
交给 HTTP client
  ↓
HTTP client 得到响应头和响应体
  ↓
你的代码拿到 JSON

14. 请求和应答的完整协议栈格式对比

ESP32 发请求

text 复制代码
Wi-Fi Frame
└── IP Packet
    └── TCP Segment
        └── HTTP Request
            ├── Request Line
            │   └── GET /data/2.5/weather?... HTTP/1.1
            ├── Headers
            │   ├── Host: api.openweathermap.org
            │   ├── User-Agent: esp32
            │   └── Connection: close
            ├── Empty Line
            └── Body: 空

服务器回响应

text 复制代码
Wi-Fi Frame
└── IP Packet
    └── TCP Segment
        └── HTTP Response
            ├── Status Line
            │   └── HTTP/1.1 200 OK
            ├── Headers
            │   ├── Content-Type: application/json
            │   ├── Content-Length: xxx
            │   └── Connection: close
            ├── Empty Line
            └── Body
                └── {"main":{"temp":23.5},"weather":[...]}

15. 加上 TCP 三次握手,完整通信长这样

HTTP 请求不是上来就发的,先要建立 TCP 连接。

text 复制代码
ESP32                                      天气服务器
  │                                             │
  │  SYN                                        │
  │────────────────────────────────────────────>│
  │                                             │
  │  SYN + ACK                                  │
  │<────────────────────────────────────────────│
  │                                             │
  │  ACK                                        │
  │────────────────────────────────────────────>│
  │                                             │
  │  HTTP GET /data/2.5/weather?...             │
  │────────────────────────────────────────────>│
  │                                             │
  │  HTTP/1.1 200 OK + JSON天气数据             │
  │<────────────────────────────────────────────│
  │                                             │
  │  FIN / ACK 关闭连接                         │
  │<───────────────────────────────────────────>│

所以真正完整顺序是:

text 复制代码
DNS 解析
TCP 三次握手
HTTP 请求
HTTP 响应
TCP 四次挥手/关闭连接

16. DNS 解析也是协议栈的一部分

你写的是域名:

text 复制代码
api.openweathermap.org

但 TCP/IP 真正连接的是 IP 地址。

所以 ESP32 要先问 DNS 服务器:

text 复制代码
api.openweathermap.org 的 IP 是多少?

DNS 查询通常是:

text 复制代码
应用层:DNS
传输层:UDP
网络层:IP
链路层:Wi-Fi

格式大概是:

text 复制代码
Wi-Fi Frame
└── IP Packet
    └── UDP Datagram
        └── DNS Query
            └── 查询 api.openweathermap.org

DNS 返回 IP 后,ESP32 才能建立 TCP 连接。


17. 如果是 HTTPS,协议栈格式变成什么?

HTTP:

text 复制代码
Wi-Fi
└── IP
    └── TCP
        └── HTTP

HTTPS:

text 复制代码
Wi-Fi
└── IP
    └── TCP
        └── TLS
            └── HTTP

也就是中间多了一层 TLS。

HTTPS 请求真实格式不是:

text 复制代码
TCP 里面直接放 HTTP GET

而是:

text 复制代码
TCP 里面放 TLS 加密数据
TLS 解密后才是 HTTP GET

18. HTTPS 天气请求封装格式

text 复制代码
Wi-Fi Frame
└── IP Packet
    └── TCP Segment
        └── TLS Record
            └── Encrypted HTTP Request
                └── GET /data/2.5/weather?... HTTP/1.1

服务器响应:

text 复制代码
Wi-Fi Frame
└── IP Packet
    └── TCP Segment
        └── TLS Record
            └── Encrypted HTTP Response
                └── HTTP/1.1 200 OK + JSON

抓包时,HTTP 可以看到明文:

http 复制代码
GET /data/2.5/weather?q=Osaka&appid=API_KEY HTTP/1.1
Host: api.openweathermap.org

HTTPS 抓包时看到的是:

text 复制代码
TLS Application Data
加密数据
加密数据
加密数据

看不到具体天气请求和 API Key。


19. HTTP 和 HTTPS 协议栈对比

项目 HTTP HTTPS
默认端口 80 443
应用层内容 明文 HTTP HTTP 被 TLS 加密
协议栈 Wi-Fi/IP/TCP/HTTP Wi-Fi/IP/TCP/TLS/HTTP
是否能抓包看到 API Key 正常看不到
ESP32 资源占用 更高
正式产品推荐 不推荐 推荐

20. 最核心的一张图

天气 HTTP 请求:

text 复制代码
ESP32 应用代码
    ↓
HTTP GET 请求字符串
    ↓
TCP 头 + HTTP 数据
    ↓
IP 头 + TCP 数据
    ↓
Wi-Fi 头 + IP 数据
    ↓
无线发送出去

天气 HTTP 响应:

text 复制代码
无线收到数据
    ↓
去掉 Wi-Fi 头
    ↓
去掉 IP 头
    ↓
TCP 重组数据
    ↓
得到 HTTP 响应
    ↓
去掉 HTTP 响应头
    ↓
得到 JSON 天气数据
    ↓
cJSON 解析

21. 你在 ESP32 代码里看到的是哪一层?

如果你用 esp_http_client

c 复制代码
esp_http_client_perform(client);

你主要操作的是:

text 复制代码
HTTP 层

底层的:

text 复制代码
DNS
TCP
IP
Wi-Fi

大部分 ESP-IDF 已经帮你处理了。

如果你用 socket 手写:

c 复制代码
socket()
connect()
send()
recv()

你操作的是:

text 复制代码
TCP 层

HTTP 请求字符串要你自己拼。

如果你用 Wi-Fi 驱动:

c 复制代码
esp_wifi_start()

你操作的是:

text 复制代码
Wi-Fi 链路层/网络接口

22. 一句话总结

天气案例中的 HTTP 协议栈格式就是:

text 复制代码
Wi-Fi 帧
  里面装 IP 包
    IP 包里面装 TCP 段
      TCP 段里面装 HTTP 请求/响应
        HTTP 响应体里面装 JSON 天气数据

最关键记住这句:

text 复制代码
HTTP 不是直接在 Wi-Fi 上跑的。
HTTP 是 TCP 的数据内容。
TCP 又被 IP 封装。
IP 最后才通过 Wi-Fi 发出去。

如果是 HTTPS,就是:

text 复制代码
Wi-Fi
  ↓
IP
  ↓
TCP
  ↓
TLS 加密层
  ↓
HTTP
  ↓
JSON 天气数据
相关推荐
汽车仪器仪表相关领域1 小时前
Kvaser Memorator R SemiPro:双通道CAN总线记录仪,汽车与工业测试的高性价比之选
大数据·网络·人工智能·功能测试·汽车·安全性测试
sdszoe49221 小时前
华为设备安全管理之路由器+ACL
网络·安全·华为·路由器+acl
Inhand陈工2 小时前
智能驾驶数据高可靠上云实战:EC3320+IR315双路冗余方案
网络·物联网·自动驾驶·智能路由器·边缘计算·腾讯云·信息与通信
byoass2 小时前
企业云盘全文检索实战:Elasticsearch集成与分布式搜索
网络·分布式·安全·elasticsearch·云计算·全文检索
云布道师2 小时前
阿里云 Tablestore 为 Hermes Agent 构建记忆系统最佳实践
网络·人工智能·阿里云
雨浓YN2 小时前
GKMLT通讯工具箱(WPF MVVM) - 02-Modbus RTU 与 TCP 报文格式、原理与CRC校验
网络·网络协议·tcp/ip
雨浓YN2 小时前
GKMLT通讯工具箱(WPF MVVM) - 01-网口/串口通讯与 ModBus RTU/TCP
网络·网络协议·tcp/ip
志栋智能4 小时前
运维超自动化:构建弹性IT架构的关键支撑
运维·服务器·网络·人工智能·架构·自动化
网安情报局10 小时前
除了 CDN,DDoS 攻击还有哪些更有效的防护方式?
网络