前言
记录下ESP32自带的HTTP接口的使用, ESP HTTP模块提供了丰富的API支持HTTP/HTTPS的请求,模块位于esp-idf\components\esp_http_client中,其中Kconfig可修改默认使能HTTPS和身份验证,在menuconfig也可以进行配置
如有异议,请指正
HTTP简介
HTTP协议是Hyper Text Transfer Protocol超文本传输协议的缩写,基于TCP传输层协议进行通信,采用客户端-服务器模型(C/S架构),属于应用层协议
HTTP数据传输是透明的,明文传输涉及到通信安全时,传输层上可套接TLS/SSL协议进行加密,也就是HTTPS
特点
- HTTP默认使用80端口,HTTPS默认使用443端口
- 无状态,协议对于事务处理没有记忆能力,每次都是一个新的连接,服务端不会记录前后的请求信息(针对这个问题,引入Cookie将记录加密存储到客户端中)
- 无连接,限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接;(HTTP1.1版本支持持久连接的方法)
- 媒体独立,允许传输任意类型的数据对象,传输类型由Content-Type定义
API
接口可参考头文件 esp-idf\components/esp_http_client/include/esp_http_client.h
esp_http_client_init
原型esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *config)
- 配置http客户端参数,分配需要的资源,返回esp_http_client_handle_t数据结构句柄,必须优先调用
esp_http_client_config_t config = {
.url = "https://www.howsmyssl.com", // url
.cert_pem = howsmyssl_com_root_cert_pem_start,//证书
};
esp_http_client_handle_t client = esp_http_client_init(&config);
- 传递配置形参,url成员必填,如果跳过证书可将元素skip_cert_common_name_check改为TRUE
/**
* @brief HTTP configuration
*/
typedef struct {
const char *url; //url请求接口必须配置 /*!< HTTP URL, the information on the URL is most important, it overrides the other fields below, if any */
const char *host; //服务器域名或ip地址 /*!< Domain or IP as string */
int port; //端口 http默认80 https 默认443 /*!< Port to connect, default depend on esp_http_client_transport_t (80 or 443) */
const char *username; //用户名,认证使用 /*!< Using for Http authentication */
const char *password; //用户密码,认证使用 /*!< Using for Http authentication */
esp_http_client_auth_type_t auth_type; //认证方式 /*!< Http authentication type, see `esp_http_client_auth_type_t` */
const char *path; //路径 /*!< HTTP Path, if not set, default is `/` */
const char *query; //请求参数 /*!< HTTP query */
const char *cert_pem; //证书 /*!< SSL server certification, PEM format as string, if the client requires to verify server */
const char *client_cert_pem; /*!< SSL client certification, PEM format as string, if the server requires to verify client */
const char *client_key_pem; /*!< SSL client key, PEM format as string, if the server requires to verify client */
esp_http_client_method_t method; //请求方式 post get /*!< HTTP Method */
int timeout_ms; //请求超时 /*!< Network timeout in milliseconds */
bool disable_auto_redirect; /*!< Disable HTTP automatic redirects */
int max_redirection_count; /*!< Max number of redirections on receiving HTTP redirect status code, using default value if zero*/
int max_authorization_retries; /*!< Max connection retries on receiving HTTP unauthorized status code, using default value if zero. Disables authorization retry if -1*/
http_event_handle_cb event_handler; //可注册回调 /*!< HTTP Event Handle */
esp_http_client_transport_t transport_type; // 传输方式 tcp ssl /*!< HTTP transport type, see `esp_http_client_transport_t` */
int buffer_size; //接收缓存大小 /*!< HTTP receive buffer size */
int buffer_size_tx; //发送缓存大小 /*!< HTTP transmit buffer size */
void *user_data; //http用户数据 /*!< HTTP user_data context */
bool is_async; //同步模式 /*!< Set asynchronous mode, only supported with HTTPS for now */
bool use_global_ca_store; /*!< Use a global ca_store for all the connections in which this bool is set. */
bool skip_cert_common_name_check; //跳过证书 /*!< Skip any validation of server certificate CN field */
} esp_http_client_config_t;
esp_http_client_perform
原型esp_err_t esp_http_client_perform(esp_http_client_handle_t client)
- 进行链路的连接与数据的传输,内部包含完整的收发断连的API调用
esp_http_client_open -> esp_http_client_write -> esp_http_client_fetch_headers -> esp_http_client_read (and option) esp_http_client_close
需要在配置esp_http_client_init后调用运行
esp_err_t err = esp_http_client_perform(client);
esp_http_client_set_post_field/esp_http_client_get_post_field
原型esp_err_t esp_http_client_set_post_field(esp_http_client_handle_t client, const char *data, int len)
-通过post方式请求的数据,传入发送数据的缓存地址与长度,必须在esp_http_client_perform
之前调用
esp_http_client_get_post_field:为获取当前当前post指向的数据缓存地址
esp_http_client_set_header/esp_http_client_get_header/esp_http_client_delete_header
- 设置、获取、删除请求头,可新增、删除或根据应用发送的数据类型来自定义请求文件类型来修改请求头
esp_http_client_set_header(client, "HeaderKey", "HeaderValue");
esp_http_client_set_method
- 设置HTTP请求方式;
//请求方式枚举
typedef enum {
HTTP_METHOD_GET = 0, /*!< HTTP GET Method */
HTTP_METHOD_POST, /*!< HTTP POST Method */
HTTP_METHOD_PUT, /*!< HTTP PUT Method */
HTTP_METHOD_PATCH, /*!< HTTP PATCH Method */
HTTP_METHOD_DELETE, /*!< HTTP DELETE Method */
HTTP_METHOD_HEAD, /*!< HTTP HEAD Method */
HTTP_METHOD_NOTIFY, /*!< HTTP NOTIFY Method */
HTTP_METHOD_SUBSCRIBE, /*!< HTTP SUBSCRIBE Method */
HTTP_METHOD_UNSUBSCRIBE,/*!< HTTP UNSUBSCRIBE Method */
HTTP_METHOD_OPTIONS, /*!< HTTP OPTIONS Method */
HTTP_METHOD_COPY, /*!< HTTP COPY Method */
HTTP_METHOD_MOVE, /*!< HTTP MOVE Method */
HTTP_METHOD_LOCK, /*!< HTTP LOCK Method */
HTTP_METHOD_UNLOCK, /*!< HTTP UNLOCK Method */
HTTP_METHOD_PROPFIND, /*!< HTTP PROPFIND Method */
HTTP_METHOD_PROPPATCH, /*!< HTTP PROPPATCH Method */
HTTP_METHOD_MKCOL, /*!< HTTP MKCOL Method */
HTTP_METHOD_MAX,
} esp_http_client_method_t;
esp_http_client_set_method(client, HTTP_METHOD_GET);//GET请求
esp_http_client_open
原型esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len)
- 打开连接,发送http请求头,形参write_len为发送内容数据长度
esp_http_client_write
- 写数据,调取esp_http_client_open成功后,可通过该接口进行数据内容的发送
esp_http_client_fetch_headers
- 读取数据,自动处理掉响应头并返回接收包长度
esp_http_client_read
- 读取http接收的数据流,返回实际读取长度;返回-1发生错误
esp_http_client_close
- 关闭http连接链路,会保留所有http请求资源
esp_http_client_cleanup
- 通过句柄esp_http_client关闭连接,释放资源,必须在esp_http_client_init初始化后使用
esp_http_client_set_redirection
- 设置重定向URL。当从服务器接收到30x代码时,客户端存储服务器提供的重定向URL。该函数将当前的网址设置为重定向,使客户端能够执行重定向请求。
esp_http_client_add_auth
- 在接收到HTTP状态代码401时,可以调用该应用编程接口来添加授权信息,调取前需清除下接收缓存;认证方式有 BASIC 认证(基本认证);DIGEST 认证(摘要认证);SSL 客户端认证;FormBase 认证(基于表单认证)
struct esp_http_client_event
- Http事件数据结构,在esp_http_client_config_t中配置对应的回调函数
typedef struct esp_http_client_event {
esp_http_client_event_id_t event_id; // 事件id /*!< event_id, to know the cause of the event */
esp_http_client_handle_t client; //句柄 /*!< esp_http_client_handle_t context */
void *data; //事件数据缓存 /*!< data of the event */
int data_len; // 事件数据长度 /*!< data length of data */
void *user_data; //用户数据 /*!< user_data context, from esp_http_client_config_t user_data */
char *header_key; //http头密钥 /*!< For HTTP_EVENT_ON_HEADER event_id, it's store current http header key */
char *header_value; // http请求头 /*!< For HTTP_EVENT_ON_HEADER event_id, it's store current http header value */
} esp_http_client_event_t;
事件Id
typedef enum {
HTTP_EVENT_ERROR = 0, //当执行期间出现任何错误时,会发生此事件 /*!< This event occurs when there are any errors during execution */
HTTP_EVENT_ON_CONNECTED, //HTTP连接到服务器 /*!< Once the HTTP has been connected to the server, no data exchange has been performed */
HTTP_EVENT_HEADERS_SENT, //发送请求头 /*!< After sending all the headers to the server */
HTTP_EVENT_HEADER_SENT = HTTP_EVENT_HEADERS_SENT, /*!< This header has been kept for backward compatability
and will be deprecated in future versions esp-idf */
HTTP_EVENT_ON_HEADER, //接收到响应头 /*!< Occurs when receiving each header sent from the server */
HTTP_EVENT_ON_DATA, //接收到数据 /*!< Occurs when receiving data from the server, possibly multiple portions of the packet */
HTTP_EVENT_ON_FINISH, //http会话完成 /*!< Occurs when finish a HTTP session */
HTTP_EVENT_DISCONNECTED, //http断开事件 /*!< The connection has been disconnected */
} esp_http_client_event_id_t;
代码实例
可参考idf目录下的test_http_client.c,路径esp-idf\components\esp_http_client\test
实例
//事件回调
static esp_err_t _http_event_handle(esp_http_client_event_t *evt)
{
switch(evt->event_id) {
case HTTP_EVENT_ERROR://错误事件
ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED://连接成功事件
ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT://发送头事件
ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER://接收头事件
ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER");
printf("%.*s", evt->data_len, (char*)evt->data);
break;
case HTTP_EVENT_ON_DATA://接收数据事件
ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
if (!esp_http_client_is_chunked_response(evt->client)) {
printf("%.*s", evt->data_len, (char*)evt->data);
}
break;
case HTTP_EVENT_ON_FINISH://会话完成事件
ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED://断开事件
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
//http client配置
esp_http_client_config_t config = {
.method = HTTP_METHOD_GET, //get请求
.url = "https://www.baidu.com/", //请求url
.event_handler = _http_event_handle,//注册时间回调
};
//测试入口
void http_client_test(void)
{
esp_http_client_handle_t client = esp_http_client_init(&config);//初始化配置
esp_err_t err = esp_http_client_perform(client);//执行请求
if(err == ESP_OK)
{
ESP_LOGI(TAG, "Status = %d, content_length = %d",
esp_http_client_get_status_code(client),//状态码
esp_http_client_get_content_length(client));//数据长度
ret = esp_http_client_read(client, pReadBuf, 512);//读取512数据内容
if(ret > 0)
{
ESP_LOGI(TAG, "recv data = %d %s", ret, pReadBuf);//打印数据
}
}
esp_http_client_cleanup(client);//断开并释放资源
}
请求响应头,内容即为百度的Html的页面
总结
乐鑫提高的接口比较完整,从服务器获取数据可直接使用上面的例程,如果是其他请求方式,如POST可以通过接口esp_http_client_set_post_field设置发送的内容数据,可使用esp_http_client_perform自动来完成收发的工作,通过事件回调可清晰跟踪当前的状态,有助于问题的调试