HTTP
HTTP Client — GET 请求 — lwIP Socket API
前置阅读:TCP/UDP
学习目标
- 理解 HTTP 在嵌入式设备上的实现方式——纯 lwIP Socket 构造 HTTP 请求报文
- 掌握 HTTP GET 请求的标准报文格式和发送流程
- 掌握
setsockopt(SO_RCVTIMEO)接收超时的配置方法 - 理解 lwIP 特有函数:
lwip_write()/lwip_read()/lwip_close()
基本概念
HTTP 实现方式
WS63 不自带 HTTP Client 库——直接用 lwIP Socket API 构造 HTTP 请求。HTTP 请求就是一段符合 RFC 2616 的文本。
HTTP GET 请求报文格式
请求行 + Header + 空行结束。空行(\r\n\r\n)是请求结束的标志。
lwIP 特有函数
| lwIP 函数 | 标准 POSIX | 说明 |
|---|---|---|
lwip_write() |
write() |
必须用 lwIP 版本 |
lwip_read() |
read() |
必须用 lwIP 版本 |
lwip_close() |
close() |
混用会导致资源泄漏 |
lwip_ioctl() |
ioctl() |
配置 socket 选项 |
通信流程
sequenceDiagram
participant W as WS63
participant D as DNS
participant S as HTTP Server
W->>D: 解析域名 → IP
D-->>W: Server IP
W->>S: socket + connect (port 80)
W->>S: lwip_write (GET 请求)
Note over W: setsockopt SO_RCVTIMEO 5s
S-->>W: lwip_read → HTTP/1.1 200 OK
W->>W: 解析 HTML body
W->>S: lwip_close
涉及 API
| API | 用途 |
|---|---|
socket(AF_INET, SOCK_STREAM, 0) |
创建 TCP socket |
connect(s, &addr, sizeof(addr)) |
连接 HTTP Server(端口 80) |
lwip_write(s, request, len) |
发送 HTTP 请求(必须用 lwIP 版本) |
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) |
设置接收超时 |
lwip_read(s, buf, len) |
读取 HTTP 响应 |
lwip_close(s) |
关闭 socket |
案例说明
做什么
WS63 WiFi 连接路由器 → DNS 解析目标 IP → HTTP GET 获取首页 HTML → 串口打印响应。
零第三方库——纯 lwIP Socket + 手动构造 HTTP 报文。
案例操作指导
第一步:编译
Kconfig 启用 lwIP + DHCP + DNS。
第二步:烧录并运行
WiFi 连接 → HTTP GET → 串口打印 HTML。
第三步:验证
串口输出 HTTP/1.1 200 OK + HTML 内容。
关键配置
| 参数 | 值 | 说明 |
|---|---|---|
| HTTP 端口 | 80 | 标准端口 |
| 接收超时 | 5s | SO_RCVTIMEO,防止永久阻塞 |
| 接收缓冲区 | 64~2048B | lwIP 默认较小 |
| 陷阱 | 必须用 lwip_close() |
混用 close() 导致资源泄漏 |
代码详解
Socket 创建与连接
int s = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = PP_HTONS(80);
addr.sin_addr.s_addr = server_ip; // DNS 解析得到
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
printf("connect failed\n");
return;
}
HTTP GET 请求构造
const char *request =
"GET / HTTP/1.1\r\n"
"Host: www.example.com\r\n"
"Connection: close\r\n"
"\r\n"; // 空行标志请求结束
lwip_write(s, request, strlen(request));
接收响应(带超时)
struct timeval tv = { .tv_sec = 5, .tv_usec = 0 };
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
char buf[2048];
int len;
while ((len = lwip_read(s, buf, sizeof(buf) - 1)) > 0) {
buf[len] = '\0';
printf("%s", buf);
}
// len == 0: 对端关闭 | len < 0: 超时或错误
响应解析
/* 检查 HTTP 状态码 */
if (strstr(buf, "HTTP/1.1 200 OK") || strstr(buf, "HTTP/1.0 200 OK")) {
/* 找 Header 结束位置(\r\n\r\n),之后是 body */
char *body = strstr(buf, "\r\n\r\n");
if (body) {
body += 4;
printf("Body: %s\n", body);
}
}