7.2.2 HTTP 协议
7.2.2 HTTP 协议
📚 本节导读
学习时长:约 25 分钟
难度级别:⭐⭐⭐☆☆
前置知识:HTTP 协议基础、JSON 格式、第 7.2.1 节 Socket 基础
🎯 学习目标
- 掌握 OneOS HTTP 客户端的使用方法
- 掌握 GET / POST / PUT / DELETE 请求的发送
- 掌握自定义请求头的设置
- 了解 HTTP 常见状态码
- 能够使用 HTTP 客户端与 RESTful API 交互
一、概述
OneOS HTTP 客户端位于 components/net/protocols/http/httpclient-v1.1.0/,是一个轻量级的 HTTP/HTTPS 客户端库,专为嵌入式设备设计。它支持 GET、POST、PUT、DELETE、HEAD 等常用 HTTP 方法,并支持自定义请求头、表单数据(http_form_data.h)和 TLS 加密传输(CONFIG_HTTP_SECURE)。
一句话核心:把"构造 HTTP 请求 → 底层 TCP/SSL 传输 → 解析 HTTP 响应"封装成 http_client_get/post/put/head/delete 等上层 API,开发者只需准备 http_client_t、http_client_data_t 两个结构体,即可发起完整的 HTTP 交互。
典型场景:
- 对接 RESTful 云平台(OneNET 等),用 GET/POST 上报遥测、下发指令;
- OTA 固件下载(
CONFIG_HTTP_DOWNLOAD下的http_client_get_file); - 与 HTTP 文件服务器交互,下载资源到本地文件系统。
选型边界:
- 需要浏览器式完整请求/响应模型时选本组件;
- 需要长时间双向、发布/订阅模型时选 MQTT(见 7.2.3);
- 资源受限、需要轻量 RESTful 的选 CoAP(见 7.2.4)。
二、核心数据结构
2.1 HTTP 请求方法(HTTP_REQUEST_TYPE)
完整源码(http_client.h):
/** @brief http requst type */
typedef enum {
HTTP_GET,
HTTP_POST,
HTTP_PUT,
HTTP_DELETE,
HTTP_HEAD
} HTTP_REQUEST_TYPE;关键理解:HTTP_GET=0、HTTP_POST=1、HTTP_PUT=2、HTTP_DELETE=3、HTTP_HEAD=4(按声明顺序自然编号)。底层 http_client_send(client, url, method, data) 的 method 参数就是该枚举值。
2.2 HTTP 客户端句柄(http_client_t)
完整源码(http_client.h):
/** @brief This structure defines the httpclient_t structure */
typedef struct {
int socket; /**< socket ID */
int remote_port; /**< hTTP or HTTPS port */
int response_code; /**< response code */
char *header; /**< request custom header */
char *auth_user; /**< username for basic authentication */
char *auth_password; /**< password for basic authentication */
bool is_http; /**< http connection? if 1, http; if 0, https */
#ifdef CONFIG_HTTP_SECURE
const char *server_cert; /**< server certification */
const char *client_cert; /**< client certification */
const char *client_pk; /**< client private key */
int server_cert_len; /**< server certification lenght, server_cert buffer size */
int client_cert_len; /**< client certification lenght, client_cert buffer size */
int client_pk_len; /**< client private key lenght, client_pk buffer size */
void *ssl; /**< ssl content */
#endif
} http_client_t;字段详解:
| 字段 | 类型 | 含义 |
|---|---|---|
socket | int | 底层 Socket 描述符(连接建立后由库填充) |
remote_port | int | 远端端口(HTTP=80、HTTPS=443,从 URL 解析得到) |
response_code | int | 最近一次响应的 HTTP 状态码(如 200),http_client_get_response_code 读取 |
header | char * | 自定义请求头字符串(由 http_client_set_custom_header 设置) |
auth_user | char * | HTTP Basic 认证用户名 |
auth_password | char * | HTTP Basic 认证密码 |
is_http | bool | 是否为 HTTP(1=HTTP,0=HTTPS),根据 URL 的 http:///https:// 判断 |
| TLS 字段 | — | 开启 CONFIG_HTTP_SECURE 时才有:server_cert/client_cert/client_pk 及各自长度、ssl 上下文 |
关键理解:应用只需
http_client_t client = {0};初始化即可,socket/remote_port/response_code/is_http由库内部填充;自定义认证通过auth_user/auth_password或header设置。
2.3 请求/响应数据(http_client_data_t)
完整源码(http_client.h):
/** @brief This structure defines the HTTP data structure. */
typedef struct {
bool is_more; /**< indicates if more data needs to be retrieved. */
bool is_chunked; /**< response data is encoded in portions/chunks.*/
int retrieve_len; /**< content length to be retrieved. */
int response_content_len; /**< response content length. */
int content_block_len; /**< the content length of one block. */
int post_buf_len; /**< post data length. */
int response_buf_len; /**< response body buffer length. */
int header_buf_len; /**< response head buffer lehgth. */
char *post_content_type; /**< content type of the post data. */
char *post_buf; /**< user data to be posted. */
char *response_buf; /**< buffer to store the response body data. */
char *header_buf; /**< buffer to store the response head data. */
bool is_redirected; /**< redirected URL? if 1, has redirect url; if 0, no redirect url */
char* redirect_url; /**< redirect url when got http 3** response code. */
int content_range_len; /**< response content range. */
} http_client_data_t;字段详解(按用途分组):
| 字段 | 含义 |
|---|---|
response_buf / response_buf_len | 响应体存储:用户提供缓冲区及大小,库把响应 body 写入(必填) |
post_buf / post_buf_len / post_content_type | POST/PUT 数据:请求体内容、长度、Content-Type(POST/PUT 时填写) |
header_buf / header_buf_len | 响应头存储(可选,用于解析响应头) |
is_more / is_chunked / retrieve_len | 分块接收控制:是否还有更多数据、是否 chunked 编码、待接收长度 |
response_content_len / content_block_len | 响应内容总长、每块长度(库内部使用) |
is_redirected / redirect_url | 重定向处理:收到 3xx 时库自动跟踪并写入重定向 URL |
content_range_len | 断点续传/Range 响应长度(文件下载用) |
关键理解(核心):
response_buf指向用户 malloc 的缓冲,response_buf_len是其大小,二者必须由应用设置(官方样例用BUF_SIZE=4096)。POST 时还需设置post_buf/post_buf_len/post_content_type。成功返回后,响应体就在client_data.response_buf中。
2.4 返回码(HTTP_RESULT_CODE)
完整源码(http.h):
/** @brief http error code */
typedef enum {
HTTP_EAGAIN = 1, /**< more data to retrieved */
HTTP_SUCCESS = 0, /**< operation success */
HTTP_ENOBUFS = -1, /**< buffer error */
HTTP_EARG = -2, /**< illegal argument */
HTTP_ENOTSUPP = -3, /**< not support */
HTTP_EDNS = -4, /**< DNS fail */
HTTP_ECONN = -5, /**< connect fail */
HTTP_ESEND = -6, /**< send packet fail */
HTTP_ECLSD = -7, /**< connect closed */
HTTP_ERECV = -8, /**< recv packet fail */
HTTP_EPARSE = -9, /**< url parse error */
HTTP_EPROTO = -10, /**< protocol error */
HTTP_EUNKOWN = -11, /**< unknown error */
HTTP_ETIMEOUT = -12, /**< timeout */
} HTTP_RESULT_CODE;关键理解:HTTP_SUCCESS(0) 表示成功;应用判断 ret >= 0 即为成功(HTTP_EAGAIN 也 >0,表示还有数据要取)。负值对应具体错误(连接、发送、接收、超时、解析等),便于定位失败环节。
三、核心 API 参考
3.1 应用层 API(推荐,http_application_api.h)
完整源码(头文件声明):
HTTP_RESULT_CODE http_client_get(http_client_t *client, const char *url, http_client_data_t *client_data);
HTTP_RESULT_CODE http_client_post(http_client_t *client, const char *url, http_client_data_t *client_data);
HTTP_RESULT_CODE http_client_put(http_client_t *client, const char *url, http_client_data_t *client_data);
HTTP_RESULT_CODE http_client_head(http_client_t *client, const char *url, http_client_data_t *client_data);
HTTP_RESULT_CODE http_client_delete(http_client_t *client, const char *url, http_client_data_t *client_data);关键理解:
- 这是面向应用的封装层,内部调用
http_request_common(见 3.2 流程),一个函数完成"连接 + 发送 + 接收"; - 注意拼写:应用层是
http_client_get(带下划线),底层原始接口是httpclient_get(无下划线,见 3.2); client_data既是输入(POST/PUT 数据、response_buf)也是输出(响应体);- 返回
HTTP_RESULT_CODE,>= 0成功。
3.2 底层 API(http_client.h)
完整源码(头文件声明):
HTTP_RESULT_CODE httpclient_get(http_client_t *client, const char *url, http_client_data_t *client_data);
HTTP_RESULT_CODE http_client_conn(http_client_t *client, const char *url);
int http_client_close(http_client_t *client);
HTTP_RESULT_CODE http_client_send(http_client_t *client, const char *url, int method, http_client_data_t *client_data);
HTTP_RESULT_CODE http_client_recv(http_client_t *client, http_client_data_t *client_data);
void http_client_set_custom_header(http_client_t *client, char *header);
int http_client_get_response_code(http_client_t *client);
int http_client_get_response_header_value(char *header_buf, char *name, int *val_pos, int *val_len);请求流程(http_request_common,http_application_api.c):
http_request_common() 流程:
├── client_data->is_redirected = 0(重置重定向标志)
├── http_client_conn(client, url) → 解析 URL、建 TCP/SSL 连接
│ └── 失败 → 返回错误(如 HTTP_ECONN)
├── http_client_send(client, url, method, client_data) → 发送请求行+头+体
│ └── 失败 → 返回发送错误
├── http_client_recv(client, client_data) → 接收并解析响应,写入 response_buf
│ └── 失败 → 返回接收/解析错误
└── 返回成功/失败关键理解:应用一般用 3.1 的应用层 API 即可;底层 API 适合需要手动控制连接/重用的高级场景。http_client_conn 会根据 URL 的 http:///https:// 前缀自动选择 TCP 或 SSL,并设置 client->is_http。
四、HTTP GET 请求示例
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "http_application_api.h"
#include "http_client.h"
#include <os_util.h>
#include <os_kernel.h>
#include <oneos_config.h>
#define BUF_SIZE 4096
void http_get_example(void)
{
http_client_t client = {0};
http_client_data_t client_data = {0};
char *buf = malloc(BUF_SIZE);
int ret;
if (buf == NULL) {
LOG_E("http", "malloc response buf failed\r\n");
return;
}
memset(buf, 0, BUF_SIZE);
/* 1. 提供响应缓冲区 */
client_data.response_buf = buf;
client_data.response_buf_len = BUF_SIZE;
/* 2. 发起 GET 请求 */
ret = http_client_get(&client, "http://iot.10086.cn/", &client_data);
if (ret < 0) {
LOG_E("http", "http get failed, ret=%d\r\n", ret);
free(buf);
return;
}
/* 3. 打印响应状态码与响应体 */
os_kprintf("[HTTP GET] status: %d\r\n", http_client_get_response_code(&client));
os_kprintf("[HTTP GET] body:\r\n%s\r\n", client_data.response_buf);
/* 4. 释放响应缓冲区 */
free(buf);
}分步详解:
http_client_t client = {0}与http_client_data_t client_data = {0}先清零,避免残留脏数据;malloc出响应缓冲并挂到client_data.response_buf/response_buf_len(必填,库会把响应体写入这里);http_client_get一步完成连接、发送 GET、接收响应;- 返回
>= 0后,http_client_get_response_code(&client)取状态码,client_data.response_buf即响应体文本; - 结束后
free(buf)释放。
五、HTTP POST 请求示例
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "http_application_api.h"
#include "http_client.h"
#include <os_kernel.h>
#include <oneos_config.h>
#define BUF_SIZE 4096
void http_post_example(void)
{
http_client_t client = {0};
http_client_data_t client_data = {0};
char *post_data = "{\"device\":\"OneOS-device-001\",\"sensor\":\"temperature\",\"value\":25.5}";
char *content_type = "application/json";
char *buf = malloc(BUF_SIZE);
int ret;
if (buf == NULL) {
LOG_E("http", "malloc response buf failed\r\n");
return;
}
memset(buf, 0, BUF_SIZE);
/* 1. 设置响应缓冲区 */
client_data.response_buf = buf;
client_data.response_buf_len = BUF_SIZE;
/* 2. 设置 POST 数据:内容、长度、Content-Type */
client_data.post_buf = post_data;
client_data.post_buf_len = strlen(post_data);
client_data.post_content_type = content_type;
/* 3. 发起 POST 请求 */
ret = http_client_post(&client, "http://121.89.166.244/server/echo", &client_data);
if (ret < 0) {
LOG_E("http", "http post failed, ret=%d\r\n", ret);
free(buf);
return;
}
/* 4. 打印响应 */
os_kprintf("[HTTP POST] status: %d\r\n", http_client_get_response_code(&client));
os_kprintf("[HTTP POST] response:\r\n%s\r\n", client_data.response_buf);
free(buf);
}分步详解:
- 与 GET 不同,POST 需额外设置
post_buf(请求体)、post_buf_len(长度)、post_content_type(Content-Type 头,如application/json); http_client_post内部按Content-Type: application/json等头 + 请求体构造 POST 报文;- 其余(response_buf、状态码读取、free)与 GET 一致。
六、HTTP PUT 与 DELETE 请求示例
/* HTTP PUT 请求 */
void http_put_example(void)
{
http_client_t client = {0};
http_client_data_t client_data = {0};
char *put_data = "1,,I am string!";
char *content_type = "text/csv";
char *buf = malloc(BUF_SIZE);
int ret;
if (buf == NULL) {
LOG_E("http", "malloc failed\r\n");
return;
}
memset(buf, 0, BUF_SIZE);
client_data.response_buf = buf;
client_data.response_buf_len = BUF_SIZE;
client_data.post_buf = put_data;
client_data.post_buf_len = strlen(put_data);
client_data.post_content_type = content_type;
ret = http_client_put(&client, "http://iot.10086.cn/", &client_data);
if (ret < 0) {
LOG_E("http", "http put failed, ret=%d\r\n", ret);
free(buf);
return;
}
os_kprintf("[HTTP PUT] status: %d\r\n", http_client_get_response_code(&client));
os_kprintf("[HTTP PUT] response:\r\n%s\r\n", client_data.response_buf);
free(buf);
}
/* HTTP DELETE 请求 */
void http_delete_example(void)
{
http_client_t client = {0};
http_client_data_t client_data = {0};
char *buf = malloc(BUF_SIZE);
int ret;
if (buf == NULL) {
LOG_E("http", "malloc failed\r\n");
return;
}
memset(buf, 0, BUF_SIZE);
client_data.response_buf = buf;
client_data.response_buf_len = BUF_SIZE;
ret = http_client_delete(&client, "http://iot.10086.cn/", &client_data);
if (ret < 0) {
LOG_E("http", "http delete failed, ret=%d\r\n", ret);
free(buf);
return;
}
os_kprintf("[HTTP DELETE] status: %d\r\n", http_client_get_response_code(&client));
os_kprintf("[HTTP DELETE] response:\r\n%s\r\n", client_data.response_buf);
free(buf);
}关键理解:PUT 与 POST 的请求体设置方式完全相同(post_buf/post_buf_len/post_content_type),区别仅在方法名与方法语义(PUT 通常用于更新资源);DELETE 通常不带请求体,只需设置 response_buf。
七、自定义请求头与 HTTPS
7.1 自定义请求头
用 http_client_set_custom_header 添加自定义请求头(如 Authorization、Host 等),需在调用 http_client_get/post/... 之前设置:
http_client_set_custom_header(&client, "Authorization: Bearer your_token\r\n");
http_client_set_custom_header(&client, "Accept: application/json\r\n");关键理解:
http_client_t.header保存自定义头字符串,库在http_client_send时把它拼接到请求头。官方样例未调用此接口时,header为NULL,请求头仅含标准字段。
7.2 HTTPS(TLS)
开启 CONFIG_HTTP_SECURE 后,http_client_t 增加证书与 ssl 上下文字段。HTTPS 请求示例(client_cert 指向 CA 证书):
#ifdef CONFIG_HTTP_SECURE
/* 证书内容较长,此处以"证书起止标记 + 省略说明"示意,完整 CA 证书见官方样例
* httpclient-sample/http_sample_test.c 的 ca_cert 数组 */
static const char *ca_cert = \
"-----BEGIN CERTIFICATE-----\r\n"
/* 在此粘贴服务器的 CA 证书 PEM 内容(Base64 编码的多行字符串) */
"-----END CERTIFICATE-----";
void https_get_example(void)
{
http_client_t client = {0};
http_client_data_t client_data = {0};
char *buf = malloc(BUF_SIZE);
if (buf == NULL) return;
memset(buf, 0, BUF_SIZE);
client.client_cert = ca_cert;
client.client_cert_len = strlen(ca_cert);
client_data.response_buf = buf;
client_data.response_buf_len = BUF_SIZE;
if (http_client_get(&client, "https://open.iot.10086.cn/", &client_data) >= 0) {
os_kprintf("HTTPS response:\r\n%s\r\n", client_data.response_buf);
}
free(buf);
}
#endif关键理解:HTTPS 与 HTTP 的调用方式完全一致,库根据 URL 的
https://前缀自动走 SSL;只需在调用前通过client_cert/client_cert_len(或server_cert)注入证书。HTTPS 依赖 mbedTLS 组件(7.3 节)。
八、HTTP 常见状态码
| 状态码 | 含义 | 说明 |
|---|---|---|
| 200 | OK | 请求成功 |
| 201 | Created | 资源创建成功(POST / PUT) |
| 204 | No Content | 请求成功但无返回内容(DELETE) |
| 301 | Moved Permanently | 资源永久重定向 |
| 302 | Found | 资源临时重定向 |
| 400 | Bad Request | 请求格式错误 |
| 401 | Unauthorized | 需要认证 |
| 403 | Forbidden | 禁止访问 |
| 404 | Not Found | 资源不存在 |
| 500 | Internal Server Error | 服务器内部错误 |
| 502 | Bad Gateway | 网关错误 |
| 503 | Service Unavailable | 服务不可用 |
关键理解:
http_client_get_response_code(&client)返回的就是上述状态码。收到 3xx(重定向)时,库会通过client_data->is_redirected标记并自动跟踪重定向(见http_client_get中的while循环)。
📝 本节小结
核心要点回顾
- 真实 API:OneOS HTTP 客户端的应用层接口是
http_client_get/post/put/head/delete(http_application_api.h),底层是http_client_conn/send/recv/close等(http_client.h),注意http_client_get(带下划线,应用层)与httpclient_get(无下划线,底层)是两个不同函数。 - 两个核心结构体:
http_client_t(连接/认证/TLS 句柄)+http_client_data_t(请求体 + 响应体数据)。 - response_buf 必填:
client_data.response_buf/response_buf_len必须由应用提供,库把响应体写入其中。 - POST/PUT 需设请求体:
post_buf/post_buf_len/post_content_type三个字段。 - 返回码:
HTTP_RESULT_CODE枚举,>= 0成功,负值对应具体错误(连接/发送/接收/超时/解析)。 - HTTPS:
CONFIG_HTTP_SECURE下设置client_cert/client_cert_len,库按 URL 前缀自动走 SSL。 - 重定向:收到 3xx 时库自动跟踪,
is_redirected/redirect_url管理。 - 文件下载:
CONFIG_HTTP_DOWNLOAD下用http_client_get_file实现 OTA/资源下载。
关键易错点速查
| 易错点 | 正确做法 |
|---|---|
用 http_client_init/request/get_body/cleanup 等 API | 这些 API 不存在 → 用真实的 http_client_get/post/... + http_client_data_t |
混淆 http_client_get 与 httpclient_get | 应用层是带下划线 http_client_get(5 参数含 client_data);底层 httpclient_get 是旧接口 |
忘记设置 response_buf | 不设则响应无处可写 → 先 malloc 并设置 response_buf/response_buf_len |
用 client.host/port/req_buf 等字段 | http_client_t 没有这些字段 → 用 remote_port/response_code/header/auth_* |
response_buf 用完不 free | 会内存泄漏 → 结束后 free(buf) 并置 response_buf = NULL |
返回判断写成 == HTTP_SUCCESS | HTTP_EAGAIN(1) 也算成功 → 用 ret >= 0 判断 |
POST 忘设 post_content_type | 服务器可能 415 或解析失败 → 设置 Content-Type(如 application/json) |
| 自定义头不设换行 | 头部字符串需以 \r\n 结尾 → "Authorization: ...\r\n" |
调用 http_client_get_response_header_value 传参混乱 | 它解析的是 header_buf 字符串,用 &val_pos/&val_len 返回位置 → 先确保 header_buf 有效 |
下一步
- 7.2.3 MQTT 协议:HTTP 是请求/响应模型,适合周期上报;下一节学习 MQTT 发布/订阅模型,掌握 OneOS MQTT 客户端的连接、主题订阅与消息收发,实现设备与云平台的长连接双向通信。