ESP32-IDF http哀求瓦解问题分析与办理

打印 上一主题 下一主题

主题 539|帖子 539|积分 1617

ESP32S3板子, 一运行http哀求百度网站的例子, 就会panic死机, 记载下出现及办理过程.
esp32s3 http哀求瓦解

一实行http哀求的perform就会瓦解,
打印如图

ESP32-IDF 的http哀求代码是根据官方demo来改的,
第一步先连接wifi,
连接上后实行http get哀求百度网站.
理论上写法是没问题的,但是运行到板子上发现很轻易瓦解.
问题代码讨论

会在可能有问题的地方注释,有4个问题点,具体看代码 ,
最主要问题是运行内存有限, 轻易发生栈或堆溢出或越界导致瓦解.
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include "freertos/FreeRTOS.h"
  6. #include "freertos/task.h"
  7. #include "esp_err.h"
  8. #include "esp_log.h"
  9. #include "esp_system.h"
  10. #include "nvs_flash.h"
  11. #include "esp_event.h"
  12. #include "esp_netif.h"
  13. #include "esp_wifi.h"
  14. #include "esp_tls.h"
  15. #include "esp_crt_bundle.h"
  16. #include "esp_http_client.h"
  17. static const char *TAG = "HTTP_REQUEST";
  18. // 这里的buffer要适当, 如果改太大了, 比如10240就可能导致死机, 要根据实际运行结果做调整
  19. #define MAX_HTTP_OUTPUT_BUFFER 2048
  20. #define HTTP_URL "http://www.baidu.com"
  21. // HTTP 请求的处理函数
  22. esp_err_t http_event_handler(esp_http_client_event_t *evt)
  23. {
  24.     // 缓存http响应的buffer
  25.     static char *output_buffer;
  26.     // 已经读取的字节数
  27.     static int output_len;
  28.     switch(evt->event_id) {
  29.         case HTTP_EVENT_ERROR:
  30.             ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
  31.             break;
  32.         case HTTP_EVENT_ON_CONNECTED:
  33.             ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
  34.             break;
  35.         case HTTP_EVENT_HEADER_SENT:
  36.             ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
  37.             break;
  38.         case HTTP_EVENT_ON_HEADER:
  39.             ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
  40.             break;
  41.         case HTTP_EVENT_ON_DATA:
  42.             ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
  43.             if (!esp_http_client_is_chunked_response(evt->client)) {
  44.                 if (evt->user_data) {
  45.                     // 问题1: 这里没有做防溢出限制, 当http返回的数据长度趤过预留的buffer大小MAX_HTTP_OUTPUT_BUFFER时就会溢出崩溃
  46.                     memcpy(evt->user_data + output_len, evt->data, evt->data_len);
  47.                 } else {
  48.                     if (output_buffer == NULL) {
  49.                         // 问题2: 这里直接用malloc申请http返回的数据长度的堆空间, 实测在esp32s3板子上跑会崩溃
  50.                         output_buffer = (char *) malloc(esp_http_client_get_content_length(evt->client));
  51.                         output_len = 0;
  52.                         if (output_buffer == NULL) {
  53.                             ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
  54.                             return ESP_FAIL;
  55.                         }
  56.                     }
  57.                     memcpy(output_buffer + output_len, evt->data, evt->data_len);
  58.                 }
  59.                 output_len += evt->data_len;
  60.             }
  61.             break;
  62.         case HTTP_EVENT_ON_FINISH:
  63.             ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
  64.             if (output_buffer != NULL) {
  65.                 // Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response
  66.                 // ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);
  67.                 free(output_buffer);
  68.                 output_buffer = NULL;
  69.             }
  70.             output_len = 0;
  71.             break;
  72.         case HTTP_EVENT_DISCONNECTED:
  73.             ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
  74.             if (output_buffer != NULL) {
  75.                 free(output_buffer);
  76.                 output_buffer = NULL;
  77.             }
  78.             output_len = 0;
  79.             break;
  80.     }
  81.     return ESP_OK;
  82. }
  83. void request(const char *url) {
  84.     printf("request  -----------1\n");
  85.     // 响应结果放在这里
  86.     char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
  87.     // 创建一个 HTTP 客户端配置
  88.     esp_http_client_config_t config = {
  89.             .method = HTTP_METHOD_GET,
  90.             .url = url,
  91.             .event_handler = http_event_handler,
  92.             .user_data = local_response_buffer,
  93.             .disable_auto_redirect = true,
  94.     };
  95.     // 创建一个 HTTP 客户端并执行 GET 请求
  96.     esp_http_client_handle_t client = esp_http_client_init(&config);
  97.     printf("request  -----------2\n");
  98.     esp_err_t err = esp_http_client_perform(client); // 请求百度网页时,一执行这行系统就会崩溃
  99.     printf("request  -----------3\n");
  100.     // 检查请求是否成功
  101.     if (err == ESP_OK) {
  102.         int len =  esp_http_client_get_content_length(client);
  103.         ESP_LOGI(TAG, "Status = %d, content_length = %d",
  104.                  esp_http_client_get_status_code(client),//状态码
  105.                  len);//数据长度
  106.     } else {
  107.         printf("HTTP GET request failed: %s\n", esp_err_to_name(err));
  108.     }
  109.     printf("Response: %.*s\n", strlen(local_response_buffer), local_response_buffer);
  110.     //断开并释放资源
  111.     esp_http_client_cleanup(client);
  112.     printf("request  -----------4\n");
  113. }
  114. void http_test_task(void *arg)
  115. {
  116.     sleep(15);
  117.     request(HTTP_URL);
  118.     vTaskDelete(NULL);
  119. }
  120. /**
  121. * @brief WiFi 的事件循环Handler
  122. * @param arg
  123. * @param event_base
  124. * @param event_id
  125. * @param event_data
  126. */
  127. void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
  128. {
  129.     printf("wifi_event_handler base:%s, id:%d\n", event_base, event_id);
  130.     if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
  131.     {
  132.         printf("esp_wifi_connect\n");
  133.         esp_wifi_connect();
  134.     }
  135.     else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
  136.     {
  137.         printf("esp_wifi_connect\n");
  138.         esp_wifi_connect();
  139.     }
  140.     if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
  141.     {
  142.         ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
  143.         ESP_LOGI("ESP32", "IP地址:: " IPSTR, IP2STR(&event->ip_info.ip));
  144.         //request(HTTP_URL); 问题点3: 获取到ip地址后,不要直接在这里执行http请求, 否则会直接崩溃
  145.     }
  146. }
  147. void app_main(void)
  148. {
  149.     esp_err_t ret = nvs_flash_init(); // 初始化默认NVS分区
  150.     if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  151.     {
  152.         ESP_ERROR_CHECK(nvs_flash_erase()); // 擦除默认NVS分区
  153.         ret = nvs_flash_init();             // 初始化默认NVS分区
  154.     }
  155.     ESP_ERROR_CHECK(ret);
  156.     ESP_ERROR_CHECK(esp_netif_init());                // 初始化底层TCP/IP堆栈
  157.     ESP_ERROR_CHECK(esp_event_loop_create_default()); // 创建默认事件循环
  158.     esp_netif_create_default_wifi_sta(); // 创建默认的WIFI STA。
  159.     wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  160.     esp_wifi_init(&cfg); // 初始化WiFi为WiFi驱动程序
  161.     wifi_sta_config_t cfg_sta = {
  162.         .ssid = "black",
  163.         .password = "black1234",
  164.         .threshold.authmode = WIFI_AUTH_WPA2_PSK,   //加密方式
  165.         .pmf_cfg = {
  166.             .capable = true,
  167.             .required = false
  168.         },
  169.     };
  170.     esp_wifi_set_config(WIFI_IF_STA, (wifi_config_t *)&cfg_sta); // 设置ESP32 STA或AP的配置
  171.     esp_wifi_set_mode(WIFI_MODE_STA); // 设置WiFi操作模式
  172.     // 将事件处理程序的实例注册到默认循环中           任何事件
  173.     esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL, NULL);
  174.     // 将事件处理程序的实例注册到默认循环中            工作站从连接的AP获得IP事件
  175.     esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL, NULL);
  176.     esp_wifi_start(); // 根据当前配置启动wifi
  177.     xTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL); //问题点4: 这里参数需要设置足够大的栈大小, 否则会导致崩溃
  178. }
复制代码
修正后不瓦解的代码

百度网页返回的http结果太多了,会导致空间不够,
针对问题代码, 做出调整, 对栈空间大小做限制, 当http返回内容过长时,直接丢弃
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include "freertos/FreeRTOS.h"
  6. #include "freertos/task.h"
  7. #include "esp_err.h"
  8. #include "esp_log.h"
  9. #include "esp_system.h"
  10. #include "nvs_flash.h"
  11. #include "esp_event.h"
  12. #include "esp_netif.h"
  13. #include "esp_wifi.h"
  14. #include "esp_tls.h"
  15. #include "esp_crt_bundle.h"
  16. #include "esp_http_client.h"
  17. static const char *TAG = "HTTP_REQUEST";
  18. #define MAX_HTTP_OUTPUT_BUFFER 2048
  19. #define HTTP_URL "http://www.baidu.com"
  20. // HTTP 请求的处理函数
  21. esp_err_t http_event_handler(esp_http_client_event_t *evt)
  22. {
  23.     // 缓存http响应的buffer
  24.     static char *output_buffer;
  25.     // 已经读取的字节数
  26.     static int output_len;
  27.     switch(evt->event_id) {
  28.         case HTTP_EVENT_ERROR:
  29.             ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
  30.             break;
  31.         case HTTP_EVENT_ON_CONNECTED:
  32.             ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
  33.             break;
  34.         case HTTP_EVENT_HEADER_SENT:
  35.             ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
  36.             break;
  37.         case HTTP_EVENT_ON_HEADER:
  38.             ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
  39.             break;
  40.         case HTTP_EVENT_ON_DATA:
  41.             ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
  42.             if (!esp_http_client_is_chunked_response(evt->client)) {
  43.                 if (evt->user_data) {
  44.                     // 这里对buffer长度进行判断, 如果http返回长度过长, 为防止溢出就丢弃,否则进行追加拷贝
  45.                     int left = MAX_HTTP_OUTPUT_BUFFER - output_len -1;
  46.                     if (left > evt->data_len)
  47.                         memcpy(evt->user_data + output_len, evt->data, evt->data_len);
  48.                     else if (left > 0)
  49.                         memcpy(evt->user_data + output_len, evt->data, left);
  50.                     else
  51.                         ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, buffer full");
  52.                 } else {
  53.                     // 如果实测user_data设置为空时,走这里申请堆内存会崩溃则可以注释掉这段,否则可以使用
  54.                     if (output_buffer == NULL) {
  55.                         output_buffer = (char *) malloc(esp_http_client_get_content_length(evt->client));
  56.                         output_len = 0;
  57.                         if (output_buffer == NULL) {
  58.                             ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
  59.                             return ESP_FAIL;
  60.                         }
  61.                     }
  62.                     memcpy(output_buffer + output_len, evt->data, evt->data_len);
  63.                 }
  64.                 output_len += evt->data_len;
  65.             }
  66.             break;
  67.         case HTTP_EVENT_ON_FINISH:
  68.             ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
  69.             if (output_buffer != NULL) {
  70.                 // Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response
  71.                 // ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);
  72.                 free(output_buffer);
  73.                 output_buffer = NULL;
  74.             }
  75.             output_len = 0;
  76.             break;
  77.         case HTTP_EVENT_DISCONNECTED:
  78.             ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
  79.             if (output_buffer != NULL) {
  80.                 free(output_buffer);
  81.                 output_buffer = NULL;
  82.             }
  83.             output_len = 0;
  84.             break;
  85.     }
  86.     return ESP_OK;
  87. }
  88. void request(const char *url) {
  89.     printf("request  -----------1\n");
  90.     // 响应结果放在这里
  91.     char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
  92.     // 创建一个 HTTP 客户端配置
  93.     esp_http_client_config_t config = {
  94.             .method = HTTP_METHOD_GET,
  95.             .url = url,
  96.             .event_handler = http_event_handler,
  97.             .user_data = local_response_buffer,
  98.             .disable_auto_redirect = true,
  99.     };
  100.     // 创建一个 HTTP 客户端并执行 GET 请求
  101.     esp_http_client_handle_t client = esp_http_client_init(&config);
  102.     printf("request  -----------2\n");
  103.     esp_err_t err = esp_http_client_perform(client); // 请求百度网页时,一执行这行系统就会崩溃
  104.     printf("request  -----------3\n");
  105.     // 检查请求是否成功
  106.     if (err == ESP_OK) {
  107.         int len =  esp_http_client_get_content_length(client);
  108.         ESP_LOGI(TAG, "Status = %d, content_length = %d",
  109.                  esp_http_client_get_status_code(client),//状态码
  110.                  len);//数据长度
  111.     } else {
  112.         printf("HTTP GET request failed: %s\n", esp_err_to_name(err));
  113.     }
  114.     printf("Response: %.*s\n", strlen(local_response_buffer), local_response_buffer);
  115.     //断开并释放资源
  116.     esp_http_client_cleanup(client);
  117.     printf("request  -----------4\n");
  118. }
  119. void http_test_task(void *arg)
  120. {
  121.     sleep(15);
  122.     request(HTTP_URL);
  123.     vTaskDelete(NULL);
  124. }
  125. /**
  126. * @brief WiFi 的事件循环Handler
  127. * @param arg
  128. * @param event_base
  129. * @param event_id
  130. * @param event_data
  131. */
  132. void wifi_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
  133. {
  134.     printf("wifi_event_handler base:%s, id:%d\n", event_base, event_id);
  135.     if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
  136.     {
  137.         printf("esp_wifi_connect\n");
  138.         esp_wifi_connect();
  139.     }
  140.     else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
  141.     {
  142.         printf("esp_wifi_connect\n");
  143.         esp_wifi_connect();
  144.     }
  145.     if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
  146.     {
  147.         ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
  148.         ESP_LOGI("ESP32", "IP地址:: " IPSTR, IP2STR(&event->ip_info.ip));
  149.         //request(HTTP_URL); 问题点2: 获取到ip地址后,不要直接在这里执行http请求, 否则会直接崩溃 , 这里注释掉不用
  150.     }
  151. }
  152. void app_main(void)
  153. {
  154.     esp_err_t ret = nvs_flash_init(); // 初始化默认NVS分区
  155.     if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  156.     {
  157.         ESP_ERROR_CHECK(nvs_flash_erase()); // 擦除默认NVS分区
  158.         ret = nvs_flash_init();             // 初始化默认NVS分区
  159.     }
  160.     ESP_ERROR_CHECK(ret);
  161.     ESP_ERROR_CHECK(esp_netif_init());                // 初始化底层TCP/IP堆栈
  162.     ESP_ERROR_CHECK(esp_event_loop_create_default()); // 创建默认事件循环
  163.     esp_netif_create_default_wifi_sta(); // 创建默认的WIFI STA。
  164.     wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  165.     esp_wifi_init(&cfg); // 初始化WiFi为WiFi驱动程序
  166.     wifi_sta_config_t cfg_sta = {
  167.         .ssid = "black",
  168.         .password = "black1234",
  169.         .threshold.authmode = WIFI_AUTH_WPA2_PSK,   //加密方式
  170.         .pmf_cfg = {
  171.             .capable = true,
  172.             .required = false
  173.         },
  174.     };
  175.     esp_wifi_set_config(WIFI_IF_STA, (wifi_config_t *)&cfg_sta); // 设置ESP32 STA或AP的配置
  176.     esp_wifi_set_mode(WIFI_MODE_STA); // 设置WiFi操作模式
  177.     // 将事件处理程序的实例注册到默认循环中           任何事件
  178.     esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL, NULL);
  179.     // 将事件处理程序的实例注册到默认循环中            工作站从连接的AP获得IP事件
  180.     esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL, NULL);
  181.     esp_wifi_start(); // 根据当前配置启动wifi
  182.     xTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL); //问题点3: 这里参数需要设置足够大的栈大小, 否则会导致崩溃, 可以根据实测来改, 最好MAX_HTTP_OUTPUT_BUFFER大,否则可能会崩溃.
  183. }
复制代码
这样连上wifi后, http get哀求百度网页就正常了, 可以打印部门内容, 发起换成简单的页面的网址, 百度返回的数据太多了.
esp32相关文章

可见我的esp32专栏
作者:帅得不敢出门 csdn原创谢绝转载

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

不到断气不罢休

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表