说明:我使用的是esp-idf-V3.1.3 ,官方给我们封装好了 HTTP,使用起来还是很方便
一、wifi连接
在main函数里面主要是做了wifi连接的初始化和HTTP任务的创建,如下是main的全部内容
void app_main()
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
app_wifi_initialise(); //wifi连接部分
xTaskCreate(&http_test_task, "http_test_task", 8192*2, NULL, 5, NULL);//创建HTTP任务
}
进入wifi初始化函数app_wifi_initialise(void),里面主要是设置连接的wifi,及启动wifi连接。
void app_wifi_initialise(void)
{
tcpip_adapter_init();//tcp协议初始化
wifi_event_group = xEventGroupCreate();//创建一个事件组
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));//wifi 事件
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
wifi_config_t wifi_config = {
.sta = {
.ssid = "TPLINK",
.password = "12345678",//设置要连接的wifi
},
};
ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));//设置模式
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));//设置连接wifi的参数
ESP_ERROR_CHECK(esp_wifi_start());//启动wifi
}
启动wifi之后就会产生一个SYSTEM_EVENT_STA_START事件,在里面开始连接wifi,连接成功之后就会产生一个事件给创建的HTTP, http是一直在等待wifi连接成功才往下执行,具体可以看下代码
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
switch (event->event_id) {
case SYSTEM_EVENT_STA_START:
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP:
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
break;
case SYSTEM_EVENT_STA_DISCONNECTED:
/* This is a workaround as ESP32 WiFi libs don't currently
auto-reassociate. */
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
break;
default:
break;
}
return ESP_OK;
}
二、HTTP 任务解析
1、http任务里面我就保留了一个http_rest();,为了自己看调试数据方便
static void http_test_task(void *pvParameters)
{
app_wifi_wait_connected();
ESP_LOGI(TAG, "Connected to AP, begin http example");
http_rest();
/* http_auth_basic();
http_auth_basic_redirect();
http_auth_digest();
http_relative_redirect();
http_absolute_redirect();
https();
http_redirect_to_https();
http_download_chunk();
http_perform_as_stream_reader();
ESP_LOGI(TAG, "Finish http example");*/
vTaskDelete(NULL);
}
2、http_rest函数,里面我只保留了POST ,而且做了修改连接到自己的服务器,代码如下
static void http_rest()
{
esp_http_client_config_t config = {
.url = "http://wjabc.wjabc.top",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
char post_data1[1024] = {0};
Get_CurrentData(post_data1);//得到JSON数据 用如 post_data
esp_http_client_set_url(client, "http://wjabc.wjabc.top/m/userAction!connect");
// esp_http_client_set_url(client, "http://wjabc.wjabc.top/m/userAction!deviceSave");
esp_http_client_set_method(client, HTTP_METHOD_POST);
esp_http_client_set_post_field(client, post_data1, strlen(post_data1));
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
int len = esp_http_client_get_content_length(client);
int read_len = 0;
char buf[1024] = {0};
read_len = esp_http_client_read(client, buf, len);
printf("\nrecv data len:%d %d %s\n",read_len,len,buf);
} else {
ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err));
}
}
这里是esp32 HTTP client 主要部分,下面详细讲解这里,因为我也是小白,写的不好或者不对的地方,还希望大家指出来
1、 esp_http_client_config_t 结构,这里主要是初始化了 URL 和http的一个事件
esp_http_client_config_t config = {
.url = "http://wjabc.wjabc.top",
.event_handler = _http_event_handler,
};
2、esp_http_client_handle_t client = esp_http_client_init(&config)函数,
作用:分配空间,初始化等
进入函数里面看看函数的全部
esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *config)
{
esp_http_client_handle_t client;
transport_handle_t tcp;
bool _success;
//第一部分分配空间
_success = (
(client = calloc(1, sizeof(esp_http_client_t))) &&
(client->parser = calloc(1, sizeof(struct http_parser))) &&
(client->parser_settings = calloc(1, sizeof(struct http_parser_settings))) &&
(client->auth_data = calloc(1, sizeof(esp_http_auth_data_t))) &&
(client->request = calloc(1, sizeof(esp_http_data_t))) &&
(client->request->headers = http_header_init()) &&
(client->request->buffer = calloc(1, sizeof(esp_http_buffer_t))) &&
(client->response = calloc(1, sizeof(esp_http_data_t))) &&
(client->response->headers = http_header_init()) &&
(client->response->buffer = calloc(1, sizeof(esp_http_buffer_t)))
);
if (!_success) {
ESP_LOGE(TAG, "Error allocate memory");
esp_http_client_cleanup(client);
return NULL;
}
//第二部分
_success = (
(client->transport_list = transport_list_init()) && //分配空间
(tcp = transport_tcp_init()) && //分配TCP空间及初始化TCP 函数
(transport_set_default_port(tcp, DEFAULT_HTTP_PORT) == ESP_OK) &&//设置默认端口为80
(transport_list_add(client->transport_list, tcp, "http") == ESP_OK)//设置默认协议为http
);
if (!_success) {
ESP_LOGE(TAG, "Error initialize transport");
esp_http_client_cleanup(client);
return NULL;
}
if (_set_config(client, config) != ESP_OK) {
ESP_LOGE(TAG, "Error set configurations");
esp_http_client_cleanup(client);
return NULL;
}
//第三部分 给请求和应答数据分配空间
_success = (
(client->request->buffer->data = malloc(client->buffer_size)) &&
(client->response->buffer->data = malloc(client->buffer_size))
);
if (!_success) {
ESP_LOGE(TAG, "Allocation failed");
esp_http_client_cleanup(client);
return NULL;
}
//第四部分 设置url和用户代理 host
_success = (
(esp_http_client_set_url(client, config->url) == ESP_OK) &&
(esp_http_client_set_header(client, "User-Agent", DEFAULT_HTTP_USER_AGENT) == ESP_OK) &&
(esp_http_client_set_header(client, "Host", client->connection_info.host) == ESP_OK)
);
if (!_success) {
ESP_LOGE(TAG, "Error set default configurations");
esp_http_client_cleanup(client);
return NULL;
}
//第五部分设置各种函数指针的值
client->parser_settings->on_message_begin = http_on_message_begin;
client->parser_settings->on_url = http_on_url;
client->parser_settings->on_status = http_on_status;
client->parser_settings->on_header_field = http_on_header_field;
client->parser_settings->on_header_value = http_on_header_value;
client->parser_settings->on_headers_complete = http_on_headers_complete;
client->parser_settings->on_body = http_on_body;
client->parser_settings->on_message_complete = http_on_message_complete;
client->parser_settings->on_chunk_complete = http_on_chunk_complete;
client->parser->data = client;
client->event.client = client;
client->state = HTTP_STATE_INIT;
return client;
}
这个里面主要重点介绍下第二部分的transport_tcp_init()函数
transport_handle_t transport_tcp_init()
{
transport_handle_t t = transport_init();
transport_tcp_t *tcp = calloc(1, sizeof(transport_tcp_t));
HTTP_MEM_CHECK(TAG, tcp, return NULL);
tcp->sock = -1;
transport_set_func(t, tcp_connect, tcp_read, tcp_write, tcp_close, tcp_poll_read, tcp_poll_write, tcp_destroy);
transport_set_context_data(t, tcp);
return t;
}
这个函数里面可以分成两部分
a、给TCP分配空间
b、设置TCP的回调函数
tcp_connect, tcp_read, tcp_write, tcp_close, tcp_poll_read, tcp_poll_write, tcp_destroy
tcp_connect函数,这个函数可以分成4部分
a、resolve_dns(host, &remote_ip) 域名解析到IP
b、tcp->sock = socket(PF_INET, SOCK_STREAM, 0); 创建socket套接字
c、setsockopt(tcp->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); 设置接收数据超时时间
d、connect(tcp->sock, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr)) != 0 连接TCP到服务器
static int tcp_connect(transport_handle_t t, const char *host, int port, int timeout_ms)
{
struct sockaddr_in remote_ip;
struct timeval tv;
transport_tcp_t *tcp = transport_get_context_data(t);
bzero(&remote_ip, sizeof(struct sockaddr_in));
//if stream_host is not ip address, resolve it AF_INET,servername,&serveraddr.sin_addr
if (inet_pton(AF_INET, host, &remote_ip.sin_addr) != 1) {
if (resolve_dns(host, &remote_ip) < 0) {//域名解析
return -1;
}
}
tcp->sock = socket(PF_INET, SOCK_STREAM, 0);//创建socket
if (tcp->sock < 0) {
ESP_LOGE(TAG, "Error create socket");
return -1;
}
remote_ip.sin_family = AF_INET;
remote_ip.sin_port = htons(port);
http_utils_ms_to_timeval(timeout_ms, &tv);
setsockopt(tcp->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));//设置接收超时
ESP_LOGD(TAG, "[sock=%d],connecting to server IP:%s,Port:%d...",
tcp->sock, ipaddr_ntoa((const ip_addr_t*)&remote_ip.sin_addr.s_addr), port);
//连接TCP
if (connect(tcp->sock, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr)) != 0) {
close(tcp->sock);
tcp->sock = -1;
return -1;
}
return tcp->sock;
}
剩下的几个大家自己看下源码,就不介绍了
3、Get_CurrentData(post_data1); 这个是我自己写的,里面就是打包json数据,esp32自带cJSON,直接使用就可以,详细代码
void Get_CurrentData(char *dat){
cJSON *root;
root = cJSON_CreateObject();
cJSON_AddStringToObject(root,"HardwareVersion","V1.0");
cJSON_AddStringToObject(root,"SoftwareVersion","V1.0");
cJSON_AddStringToObject(root,"OnlyID","112233445566");
cJSON_AddStringToObject(root,"Status","true");
char *out = cJSON_Print(root);
memcpy(dat,out,strlen(out));
cJSON_Delete(root);
free(out);
}
4、esp_http_client_set_url(client, "http://wjabc.wjabc.top/m/userAction!connect"); 函数,这个函数主要是解析url ,然后得到各个字段,下面是这里详细介绍了URL的组成
URL(Uniform Resource Locator) 地址用于描述一个网络上的资源, 基本格式如下
schema://host[:port#]/path/.../[;url-params][?query-string][#anchor]
scheme 指定低层使用的协议(例如:http, https, ftp)
host HTTP服务器的IP地址或者域名
port# HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,必须指明,例如 http://www.cnblogs.com:8080/
path 访问资源的路径
url-params
query-string 发送给http服务器的数据
anchor- 锚
URL 的一个例子
http://www.mywebsite.com/sj/test;id=8079?name=sviergn&x=true#stuffSchema: http
host: www.mywebsite.com
path: /sj/test
URL params: id=8079
Query String: name=sviergn&x=true
Anchor: stuff
esp_err_t esp_http_client_set_url(esp_http_client_handle_t client, const char *url)
{
char *old_host = NULL;
struct http_parser_url purl;
int old_port;
if (client == NULL || url == NULL) {
ESP_LOGE(TAG, "client or url must not NULL");
return ESP_ERR_INVALID_ARG;
}
http_parser_url_init(&purl);
printf("----------------------http_client_set_url--------------------------\n");
int parser_status = http_parser_parse_url(url, strlen(url), 0, &purl);
if (parser_status != 0) {
ESP_LOGE(TAG, "Error parse url %s", url);
return ESP_ERR_INVALID_ARG;
}
old_host = client->connection_info.host;
old_port = client->connection_info.port;
if(old_host != old_host){
printf("old_host:%s\n",old_host);
}else{
printf("old_host:%s\n","NULL");
}
printf("old_port:%d\n",old_port);
if (purl.field_data[UF_HOST].len) {
http_utils_assign_string(&client->connection_info.host, url + purl.field_data[UF_HOST].off, purl.field_data[UF_HOST].len);
HTTP_MEM_CHECK(TAG, client->connection_info.host, return ESP_ERR_NO_MEM);
}
printf("host:%s\n",client->connection_info.host);
// Close the connection if host was changed
if (old_host && client->connection_info.host
&& strcasecmp(old_host, (const void *)client->connection_info.host) != 0) {
ESP_LOGD(TAG, "New host assign = %s", client->connection_info.host);
if (esp_http_client_set_header(client, "Host", client->connection_info.host) != ESP_OK) {
return ESP_ERR_NO_MEM;
}
esp_http_client_close(client);
}
printf("host:%s\n",client->connection_info.host);
if (purl.field_data[UF_SCHEMA].len) {
http_utils_assign_string(&client->connection_info.scheme, url + purl.field_data[UF_SCHEMA].off, purl.field_data[UF_SCHEMA].len);
HTTP_MEM_CHECK(TAG, client->connection_info.scheme, return ESP_ERR_NO_MEM);
if (strcasecmp(client->connection_info.scheme, "http") == 0) {
client->connection_info.port = DEFAULT_HTTP_PORT;
} else if (strcasecmp(client->connection_info.scheme, "https") == 0) {
client->connection_info.port = DEFAULT_HTTPS_PORT;
}
}
printf("scheme:%s\n",client->connection_info.scheme);
printf("port:%d\n",client->connection_info.port);
if (purl.field_data[UF_PORT].len) {
client->connection_info.port = strtol((const char*)(url + purl.field_data[UF_PORT].off), NULL, 10);
}
if (old_port != client->connection_info.port) {
esp_http_client_close(client);
}
if (purl.field_data[UF_USERINFO].len) {
char *user_info = NULL;
http_utils_assign_string(&user_info, url + purl.field_data[UF_USERINFO].off, purl.field_data[UF_USERINFO].len);
if (user_info) {
char *username = user_info;
char *password = strchr(user_info, ':');
if (password) {
*password = 0;
password ++;
http_utils_assign_string(&client->connection_info.password, password, 0);
HTTP_MEM_CHECK(TAG, client->connection_info.password, return ESP_ERR_NO_MEM);
}
http_utils_assign_string(&client->connection_info.username, username, 0);
HTTP_MEM_CHECK(TAG, client->connection_info.username, return ESP_ERR_NO_MEM);
free(user_info);
} else {
return ESP_ERR_NO_MEM;
}
} else {
free(client->connection_info.username);
free(client->connection_info.password);
client->connection_info.username = NULL;
client->connection_info.password = NULL;
}
if(client->connection_info.username != NULL){
printf("username:%s\n",client->connection_info.username);
}else{
printf("username:%s\n","NULL");
}
if(client->connection_info.password != NULL){
printf("password:%s\n",client->connection_info.password);
}else{
printf("password:%s\n","NULL");
}
//Reset path and query if there are no information
if (purl.field_data[UF_PATH].len) {
http_utils_assign_string(&client->connection_info.path, url + purl.field_data[UF_PATH].off, purl.field_data[UF_PATH].len);
} else {
http_utils_assign_string(&client->connection_info.path, "/", 0);
}
printf("path:%s\n",client->connection_info.path);
HTTP_MEM_CHECK(TAG, client->connection_info.path, return ESP_ERR_NO_MEM);
if (purl.field_data[UF_QUERY].len) {
http_utils_assign_string(&client->connection_info.query, url + purl.field_data[UF_QUERY].off, purl.field_data[UF_QUERY].len);
HTTP_MEM_CHECK(TAG, client->connection_info.query, return ESP_ERR_NO_MEM);
} else if (client->connection_info.query) {
free(client->connection_info.query);
client->connection_info.query = NULL;
}
if(client->connection_info.query != NULL){
printf("query:%s\n",client->connection_info.query);
}else{
printf("query:%s\n","NULL");
}
return ESP_OK;
}
esp_http_client_set_url 源码比较多,大家自己仔细看看,我用url = http://wjabc.wjabc.top/m/userAction!connect 作为参数
解析得到的数据:
old_host:NULL
old_port:80
host:wjabc.wjabc.top
host:wjabc.wjabc.top
scheme:http
port:80
username:NULL
password:NULL
path:/m/userAction!connect
query:NULL
这些数据都是赋值给了connection_info_t结构体的成员
typedef struct {
char *url;
char *scheme;
char *host;
int port;
char *username;
char *password;
char *path;
char *query;
char *cert_pem;
esp_http_client_method_t method;
esp_http_client_auth_type_t auth_type;
esp_http_client_transport_t transport_type;
int max_store_header_size;
} connection_info_t;
5、esp_http_client_set_method(client, HTTP_METHOD_POST);函数,这个函数就比较简单了
作用:设置请求方式为POST
esp_err_t esp_http_client_set_method(esp_http_client_handle_t client, esp_http_client_method_t method)
{
client->connection_info.method = method;
return ESP_OK;
}
可以看到 也是给上面的结构体复制,
6、esp_http_client_set_post_field(client, post_data1, strlen(post_data1));函数,
作用:设置url,解析参数
esp_err_t esp_http_client_set_post_field(esp_http_client_handle_t client, const char *data, int len)
{
esp_err_t err = ESP_OK;
client->post_data = (char *)data;
client->post_len = len;
ESP_LOGD(TAG, "set post file length = %d", len);
if (client->post_data) {
err = esp_http_client_set_header(client, "Content-Type", "application/json");
// err = esp_http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
} else {
client->post_len = 0;
err = esp_http_client_set_header(client, "Content-Type", NULL);
}
return err;
}
这个函数首先设置了HTTP请求的数据body,这个数据是第二步得到的JSON数据,然后下面设置了Content-Type内容编码,我们这里是cJSON,所以设置为application/json,注释掉原来的了。
7、esp_http_client_perform(client); 这个函数是重点,完成TCP发送和接收,主要说下三个部分
esp_err_t esp_http_client_perform(esp_http_client_handle_t client)
{
esp_err_t err;
do { //第一部分
if ((err = esp_http_client_open(client, client->post_len)) != ESP_OK) {
return err;
}
if (client->post_data && client->post_len) {
printf("\r\nclient_open post_data len:%d %s\r\n\n",client->post_len,client->post_data);
//第二部分
if (esp_http_client_write(client, client->post_data, client->post_len) <= 0) {
ESP_LOGE(TAG, "Error upload data");
return ESP_ERR_HTTP_WRITE_DATA;
}
}
第三大部分
int lenth = esp_http_client_fetch_headers(client);
if (lenth < 0) {
return ESP_ERR_HTTP_FETCH_HEADER;
}
printf("\n 12recv Data len:%d %s\n\n",client->response->buffer->len,client->response->buffer->data);
if ((err = esp_http_check_response(client)) != ESP_OK) {
ESP_LOGE(TAG, "Error response");
return err;
}
while (client->response->is_chunked && !client->is_chunk_complete) {
if (esp_http_client_get_data(client) <= 0) {
ESP_LOGD(TAG, "Read finish or server requests close");
break;
}
}
while (client->response->data_process < client->response->content_length) {
if (esp_http_client_get_data(client) <= 0) {
ESP_LOGD(TAG, "Read finish or server requests close");
break;
}
}
http_dispatch_event(client, HTTP_EVENT_ON_FINISH, NULL, 0);
if (!http_should_keep_alive(client->parser)) {
ESP_LOGD(TAG, "Close connection");
esp_http_client_close(client);
} else {
if (client->state > HTTP_STATE_CONNECTED) {
client->state = HTTP_STATE_CONNECTED;
}
}
} while (client->process_again);
return ESP_OK;
}
a、esp_http_client_open(client, client->post_len)) 函数
作用:连接TCP,并发送HTTP头
esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len)
{
esp_err_t err;
if (client->state == HTTP_STATE_UNINIT) {
ESP_LOGE(TAG, "Client has not been initialized");
return ESP_ERR_INVALID_STATE;
}
if ((err = esp_http_client_prepare(client)) != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize request data");
esp_http_client_close(client);
return err;
}
if (client->state < HTTP_STATE_CONNECTED) {
ESP_LOGD(TAG, "Begin connect to: %s://%s:%d", client->connection_info.scheme, client->connection_info.host, client->connection_info.port);
client->transport = transport_list_get_transport(client->transport_list, client->connection_info.scheme);
if (client->transport == NULL) {
ESP_LOGE(TAG, "No transport found");
return ESP_ERR_HTTP_INVALID_TRANSPORT;
}
//第一部分连接TCP
if (transport_connect(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms) < 0) {
ESP_LOGE(TAG, "Connection failed, sock < 0");
return ESP_ERR_HTTP_CONNECT;
}
http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0);//产生一个连接成功事件
client->state = HTTP_STATE_CONNECTED;
}
if (write_len >= 0) {
http_header_set_format(client->request->headers, "Content-Length", "%d", write_len);
} else if (write_len < 0) {
esp_http_client_set_header(client, "Transfer-Encoding", "chunked");
esp_http_client_set_method(client, HTTP_METHOD_POST);
}
int header_index = 0;
int wlen = client->buffer_size;
const char *method = HTTP_METHOD_MAPPING[client->connection_info.method];
int first_line = snprintf(client->request->buffer->data,
client->buffer_size, "%s %s",
method,
client->connection_info.path);
if (first_line > client->buffer_size) {
ESP_LOGE(TAG, "Out of buffer");
return ESP_ERR_HTTP_CONNECT;
}
printf("\n---->:%s\n",client->request->buffer->data);
if (client->connection_info.query) {
first_line += snprintf(client->request->buffer->data + first_line,
client->buffer_size - first_line, "?%s", client->connection_info.query);
if (first_line > client->buffer_size) {
ESP_LOGE(TAG, "Out of buffer");
return ESP_ERR_HTTP_CONNECT;
}
}
printf("\n---->:%s\n",client->request->buffer->data);
first_line += snprintf(client->request->buffer->data + first_line,
client->buffer_size - first_line, " %s\r\n", DEFAULT_HTTP_PROTOCOL);
if (first_line > client->buffer_size) {
ESP_LOGE(TAG, "Out of buffer");
return ESP_ERR_HTTP_CONNECT;
}
wlen -= first_line;
while ((header_index = http_header_generate_string(client->request->headers, header_index, client->request->buffer->data + first_line, &wlen))) {
if (wlen <= 0) {
break;
}
if (first_line) {
wlen += first_line;
first_line = 0;
}
client->request->buffer->data[wlen] = 0;
ESP_LOGD(TAG, "Write header[%d]: %s", header_index, client->request->buffer->data);
printf("\r\nclient_open Write Data len:%d %s\r\n\n",wlen,client->request->buffer->data);
//第二部分 发送http头
if (transport_write(client->transport, client->request->buffer->data, wlen, client->timeout_ms) <= 0) {
ESP_LOGE(TAG, "Error write request");
esp_http_client_close(client);
return ESP_ERR_HTTP_WRITE_DATA;
}
wlen = client->buffer_size;
}
client->state = HTTP_STATE_REQ_COMPLETE_HEADER;
return ESP_OK;
}
函数源码比较长,这个里面主要可以分成两部分,其它基本是组包,组成HTTP头
1、transport_connect(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms)
到这里才调用TCP 连接服务器,连接成功之后通过http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0)发送一个事件给HTTP的事件函数
2、transport_write(client->transport, client->request->buffer->data, wlen, client->timeout_ms)
到这是连接成功之后发送http头数据,esp32是 http头和body分开发送的
b、esp_http_client_write(client, client->post_data, client->post_len)
这个函数是发送body的函数,详细见下图,上面部分是esp_http_client_open函数里面发送的,下面的是esp_http_client_write发送的。
c、int esp_http_client_fetch_headers(esp_http_client_handle_t client)
http发送之后,调用esp_http_client_fetch_headers等待接收数据,
int esp_http_client_fetch_headers(esp_http_client_handle_t client)
{
if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) {
return ESP_FAIL;
}
client->state = HTTP_STATE_REQ_COMPLETE_DATA;
esp_http_buffer_t *buffer = client->response->buffer;
client->response->status_code = -1;
while (client->state < HTTP_STATE_RES_COMPLETE_HEADER) {
buffer->len = transport_read(client->transport, buffer->data, client->buffer_size, client->timeout_ms);
if (buffer->len <= 0) {
return ESP_FAIL;
}
http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len);
}
buffer->data[buffer->len] = 0;
ESP_LOGD(TAG, "content_length = %d", client->response->content_length);
if (client->response->content_length <= 0) {
client->response->is_chunked = true;
return 0;
}
return client->response->content_length;
}
这个函数比较简单,就是在里面一直等待http返回应答数据,里面设置了接收超时,接收到数据就退出,下面图就是接收到的http应答
8、 esp_http_client_get_status_code(client)
作用:判断http应答状态 正常是200
9、esp_http_client_get_content_length(client))
作用:得到body的数据长度
10、esp_http_client_read(client, buf, len);
作用:获取HTTP 返回的body数据
下面就是得到的数据
11、 esp_http_client_cleanup(client);
作用:用的最后一个函数。它将关闭连接(如果有的话)并释放分配给HTTP客户端的所有内存
到这里基本结束了,写了2个多小时,深夜2.17