当前位置:   article > 正文

ESP32 的esp_http_client详解

ESP32 的esp_http_client详解

说明:我使用的是esp-idf-V3.1.3 ,官方给我们封装好了 HTTP,使用起来还是很方便

一、wifi连接

在main函数里面主要是做了wifi连接的初始化和HTTP任务的创建,如下是main的全部内容
 

  1. void app_main()
  2. {
  3. esp_err_t ret = nvs_flash_init();
  4. if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
  5. ESP_ERROR_CHECK(nvs_flash_erase());
  6. ret = nvs_flash_init();
  7. }
  8. ESP_ERROR_CHECK(ret);
  9. app_wifi_initialise(); //wifi连接部分
  10. xTaskCreate(&http_test_task, "http_test_task", 8192*2, NULL, 5, NULL);//创建HTTP任务
  11. }

进入wifi初始化函数app_wifi_initialise(void),里面主要是设置连接的wifi,及启动wifi连接。

  1. void app_wifi_initialise(void)
  2. {
  3. tcpip_adapter_init();//tcp协议初始化
  4. wifi_event_group = xEventGroupCreate();//创建一个事件组
  5. ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));//wifi 事件
  6. wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  7. ESP_ERROR_CHECK(esp_wifi_init(&cfg));
  8. ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
  9. wifi_config_t wifi_config = {
  10. .sta = {
  11. .ssid = "TPLINK",
  12. .password = "12345678",//设置要连接的wifi
  13. },
  14. };
  15. ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
  16. ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));//设置模式
  17. ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));//设置连接wifi的参数
  18. ESP_ERROR_CHECK(esp_wifi_start());//启动wifi
  19. }

启动wifi之后就会产生一个SYSTEM_EVENT_STA_START事件,在里面开始连接wifi,连接成功之后就会产生一个事件给创建的HTTP, http是一直在等待wifi连接成功才往下执行,具体可以看下代码

  1. static esp_err_t event_handler(void *ctx, system_event_t *event)
  2. {
  3. switch (event->event_id) {
  4. case SYSTEM_EVENT_STA_START:
  5. esp_wifi_connect();
  6. break;
  7. case SYSTEM_EVENT_STA_GOT_IP:
  8. xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
  9. break;
  10. case SYSTEM_EVENT_STA_DISCONNECTED:
  11. /* This is a workaround as ESP32 WiFi libs don't currently
  12. auto-reassociate. */
  13. esp_wifi_connect();
  14. xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
  15. break;
  16. default:
  17. break;
  18. }
  19. return ESP_OK;
  20. }

二、HTTP 任务解析

1、http任务里面我就保留了一个http_rest();,为了自己看调试数据方便

  1. static void http_test_task(void *pvParameters)
  2. {
  3. app_wifi_wait_connected();
  4. ESP_LOGI(TAG, "Connected to AP, begin http example");
  5. http_rest();
  6. /* http_auth_basic();
  7. http_auth_basic_redirect();
  8. http_auth_digest();
  9. http_relative_redirect();
  10. http_absolute_redirect();
  11. https();
  12. http_redirect_to_https();
  13. http_download_chunk();
  14. http_perform_as_stream_reader();
  15. ESP_LOGI(TAG, "Finish http example");*/
  16. vTaskDelete(NULL);
  17. }

2、http_rest函数,里面我只保留了POST ,而且做了修改连接到自己的服务器,代码如下

  1. static void http_rest()
  2. {
  3. esp_http_client_config_t config = {
  4. .url = "http://wjabc.wjabc.top",
  5. .event_handler = _http_event_handler,
  6. };
  7. esp_http_client_handle_t client = esp_http_client_init(&config);
  8. char post_data1[1024] = {0};
  9. Get_CurrentData(post_data1);//得到JSON数据 用如 post_data
  10. esp_http_client_set_url(client, "http://wjabc.wjabc.top/m/userAction!connect");
  11. // esp_http_client_set_url(client, "http://wjabc.wjabc.top/m/userAction!deviceSave");
  12. esp_http_client_set_method(client, HTTP_METHOD_POST);
  13. esp_http_client_set_post_field(client, post_data1, strlen(post_data1));
  14. esp_err_t err = esp_http_client_perform(client);
  15. if (err == ESP_OK) {
  16. ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %d",
  17. esp_http_client_get_status_code(client),
  18. esp_http_client_get_content_length(client));
  19. int len = esp_http_client_get_content_length(client);
  20. int read_len = 0;
  21. char buf[1024] = {0};
  22. read_len = esp_http_client_read(client, buf, len);
  23. printf("\nrecv data len:%d %d %s\n",read_len,len,buf);
  24. } else {
  25. ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err));
  26. }
  27. }

这里是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)函数,

作用:分配空间,初始化等

进入函数里面看看函数的全部

  1. esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *config)
  2. {
  3. esp_http_client_handle_t client;
  4. transport_handle_t tcp;
  5. bool _success;
  6. //第一部分分配空间
  7. _success = (
  8. (client = calloc(1, sizeof(esp_http_client_t))) &&
  9. (client->parser = calloc(1, sizeof(struct http_parser))) &&
  10. (client->parser_settings = calloc(1, sizeof(struct http_parser_settings))) &&
  11. (client->auth_data = calloc(1, sizeof(esp_http_auth_data_t))) &&
  12. (client->request = calloc(1, sizeof(esp_http_data_t))) &&
  13. (client->request->headers = http_header_init()) &&
  14. (client->request->buffer = calloc(1, sizeof(esp_http_buffer_t))) &&
  15. (client->response = calloc(1, sizeof(esp_http_data_t))) &&
  16. (client->response->headers = http_header_init()) &&
  17. (client->response->buffer = calloc(1, sizeof(esp_http_buffer_t)))
  18. );
  19. if (!_success) {
  20. ESP_LOGE(TAG, "Error allocate memory");
  21. esp_http_client_cleanup(client);
  22. return NULL;
  23. }
  24. //第二部分
  25. _success = (
  26. (client->transport_list = transport_list_init()) && //分配空间
  27. (tcp = transport_tcp_init()) && //分配TCP空间及初始化TCP 函数
  28. (transport_set_default_port(tcp, DEFAULT_HTTP_PORT) == ESP_OK) &&//设置默认端口为80
  29. (transport_list_add(client->transport_list, tcp, "http") == ESP_OK)//设置默认协议为http
  30. );
  31. if (!_success) {
  32. ESP_LOGE(TAG, "Error initialize transport");
  33. esp_http_client_cleanup(client);
  34. return NULL;
  35. }
  36. if (_set_config(client, config) != ESP_OK) {
  37. ESP_LOGE(TAG, "Error set configurations");
  38. esp_http_client_cleanup(client);
  39. return NULL;
  40. }
  41. //第三部分 给请求和应答数据分配空间
  42. _success = (
  43. (client->request->buffer->data = malloc(client->buffer_size)) &&
  44. (client->response->buffer->data = malloc(client->buffer_size))
  45. );
  46. if (!_success) {
  47. ESP_LOGE(TAG, "Allocation failed");
  48. esp_http_client_cleanup(client);
  49. return NULL;
  50. }
  51. //第四部分 设置url和用户代理 host
  52. _success = (
  53. (esp_http_client_set_url(client, config->url) == ESP_OK) &&
  54. (esp_http_client_set_header(client, "User-Agent", DEFAULT_HTTP_USER_AGENT) == ESP_OK) &&
  55. (esp_http_client_set_header(client, "Host", client->connection_info.host) == ESP_OK)
  56. );
  57. if (!_success) {
  58. ESP_LOGE(TAG, "Error set default configurations");
  59. esp_http_client_cleanup(client);
  60. return NULL;
  61. }
  62. //第五部分设置各种函数指针的值
  63. client->parser_settings->on_message_begin = http_on_message_begin;
  64. client->parser_settings->on_url = http_on_url;
  65. client->parser_settings->on_status = http_on_status;
  66. client->parser_settings->on_header_field = http_on_header_field;
  67. client->parser_settings->on_header_value = http_on_header_value;
  68. client->parser_settings->on_headers_complete = http_on_headers_complete;
  69. client->parser_settings->on_body = http_on_body;
  70. client->parser_settings->on_message_complete = http_on_message_complete;
  71. client->parser_settings->on_chunk_complete = http_on_chunk_complete;
  72. client->parser->data = client;
  73. client->event.client = client;
  74. client->state = HTTP_STATE_INIT;
  75. return client;
  76. }

这个里面主要重点介绍下第二部分的transport_tcp_init()函数

 

  1. transport_handle_t transport_tcp_init()
  2. {
  3. transport_handle_t t = transport_init();
  4. transport_tcp_t *tcp = calloc(1, sizeof(transport_tcp_t));
  5. HTTP_MEM_CHECK(TAG, tcp, return NULL);
  6. tcp->sock = -1;
  7. transport_set_func(t, tcp_connect, tcp_read, tcp_write, tcp_close, tcp_poll_read, tcp_poll_write, tcp_destroy);
  8. transport_set_context_data(t, tcp);
  9. return t;
  10. }

这个函数里面可以分成两部分

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到服务器

  1. static int tcp_connect(transport_handle_t t, const char *host, int port, int timeout_ms)
  2. {
  3. struct sockaddr_in remote_ip;
  4. struct timeval tv;
  5. transport_tcp_t *tcp = transport_get_context_data(t);
  6. bzero(&remote_ip, sizeof(struct sockaddr_in));
  7. //if stream_host is not ip address, resolve it AF_INET,servername,&serveraddr.sin_addr
  8. if (inet_pton(AF_INET, host, &remote_ip.sin_addr) != 1) {
  9. if (resolve_dns(host, &remote_ip) < 0) {//域名解析
  10. return -1;
  11. }
  12. }
  13. tcp->sock = socket(PF_INET, SOCK_STREAM, 0);//创建socket
  14. if (tcp->sock < 0) {
  15. ESP_LOGE(TAG, "Error create socket");
  16. return -1;
  17. }
  18. remote_ip.sin_family = AF_INET;
  19. remote_ip.sin_port = htons(port);
  20. http_utils_ms_to_timeval(timeout_ms, &tv);
  21. setsockopt(tcp->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));//设置接收超时
  22. ESP_LOGD(TAG, "[sock=%d],connecting to server IP:%s,Port:%d...",
  23. tcp->sock, ipaddr_ntoa((const ip_addr_t*)&remote_ip.sin_addr.s_addr), port);
  24. //连接TCP
  25. if (connect(tcp->sock, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr)) != 0) {
  26. close(tcp->sock);
  27. tcp->sock = -1;
  28. return -1;
  29. }
  30. return tcp->sock;
  31. }

剩下的几个大家自己看下源码,就不介绍了

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详解

  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#stuff

Schema: http

host: www.mywebsite.com

path: /sj/test

URL params: id=8079

Query String: name=sviergn&x=true

Anchor: stuff

  1. esp_err_t esp_http_client_set_url(esp_http_client_handle_t client, const char *url)
  2. {
  3. char *old_host = NULL;
  4. struct http_parser_url purl;
  5. int old_port;
  6. if (client == NULL || url == NULL) {
  7. ESP_LOGE(TAG, "client or url must not NULL");
  8. return ESP_ERR_INVALID_ARG;
  9. }
  10. http_parser_url_init(&purl);
  11. printf("----------------------http_client_set_url--------------------------\n");
  12. int parser_status = http_parser_parse_url(url, strlen(url), 0, &purl);
  13. if (parser_status != 0) {
  14. ESP_LOGE(TAG, "Error parse url %s", url);
  15. return ESP_ERR_INVALID_ARG;
  16. }
  17. old_host = client->connection_info.host;
  18. old_port = client->connection_info.port;
  19. if(old_host != old_host){
  20. printf("old_host:%s\n",old_host);
  21. }else{
  22. printf("old_host:%s\n","NULL");
  23. }
  24. printf("old_port:%d\n",old_port);
  25. if (purl.field_data[UF_HOST].len) {
  26. http_utils_assign_string(&client->connection_info.host, url + purl.field_data[UF_HOST].off, purl.field_data[UF_HOST].len);
  27. HTTP_MEM_CHECK(TAG, client->connection_info.host, return ESP_ERR_NO_MEM);
  28. }
  29. printf("host:%s\n",client->connection_info.host);
  30. // Close the connection if host was changed
  31. if (old_host && client->connection_info.host
  32. && strcasecmp(old_host, (const void *)client->connection_info.host) != 0) {
  33. ESP_LOGD(TAG, "New host assign = %s", client->connection_info.host);
  34. if (esp_http_client_set_header(client, "Host", client->connection_info.host) != ESP_OK) {
  35. return ESP_ERR_NO_MEM;
  36. }
  37. esp_http_client_close(client);
  38. }
  39. printf("host:%s\n",client->connection_info.host);
  40. if (purl.field_data[UF_SCHEMA].len) {
  41. http_utils_assign_string(&client->connection_info.scheme, url + purl.field_data[UF_SCHEMA].off, purl.field_data[UF_SCHEMA].len);
  42. HTTP_MEM_CHECK(TAG, client->connection_info.scheme, return ESP_ERR_NO_MEM);
  43. if (strcasecmp(client->connection_info.scheme, "http") == 0) {
  44. client->connection_info.port = DEFAULT_HTTP_PORT;
  45. } else if (strcasecmp(client->connection_info.scheme, "https") == 0) {
  46. client->connection_info.port = DEFAULT_HTTPS_PORT;
  47. }
  48. }
  49. printf("scheme:%s\n",client->connection_info.scheme);
  50. printf("port:%d\n",client->connection_info.port);
  51. if (purl.field_data[UF_PORT].len) {
  52. client->connection_info.port = strtol((const char*)(url + purl.field_data[UF_PORT].off), NULL, 10);
  53. }
  54. if (old_port != client->connection_info.port) {
  55. esp_http_client_close(client);
  56. }
  57. if (purl.field_data[UF_USERINFO].len) {
  58. char *user_info = NULL;
  59. http_utils_assign_string(&user_info, url + purl.field_data[UF_USERINFO].off, purl.field_data[UF_USERINFO].len);
  60. if (user_info) {
  61. char *username = user_info;
  62. char *password = strchr(user_info, ':');
  63. if (password) {
  64. *password = 0;
  65. password ++;
  66. http_utils_assign_string(&client->connection_info.password, password, 0);
  67. HTTP_MEM_CHECK(TAG, client->connection_info.password, return ESP_ERR_NO_MEM);
  68. }
  69. http_utils_assign_string(&client->connection_info.username, username, 0);
  70. HTTP_MEM_CHECK(TAG, client->connection_info.username, return ESP_ERR_NO_MEM);
  71. free(user_info);
  72. } else {
  73. return ESP_ERR_NO_MEM;
  74. }
  75. } else {
  76. free(client->connection_info.username);
  77. free(client->connection_info.password);
  78. client->connection_info.username = NULL;
  79. client->connection_info.password = NULL;
  80. }
  81. if(client->connection_info.username != NULL){
  82. printf("username:%s\n",client->connection_info.username);
  83. }else{
  84. printf("username:%s\n","NULL");
  85. }
  86. if(client->connection_info.password != NULL){
  87. printf("password:%s\n",client->connection_info.password);
  88. }else{
  89. printf("password:%s\n","NULL");
  90. }
  91. //Reset path and query if there are no information
  92. if (purl.field_data[UF_PATH].len) {
  93. http_utils_assign_string(&client->connection_info.path, url + purl.field_data[UF_PATH].off, purl.field_data[UF_PATH].len);
  94. } else {
  95. http_utils_assign_string(&client->connection_info.path, "/", 0);
  96. }
  97. printf("path:%s\n",client->connection_info.path);
  98. HTTP_MEM_CHECK(TAG, client->connection_info.path, return ESP_ERR_NO_MEM);
  99. if (purl.field_data[UF_QUERY].len) {
  100. http_utils_assign_string(&client->connection_info.query, url + purl.field_data[UF_QUERY].off, purl.field_data[UF_QUERY].len);
  101. HTTP_MEM_CHECK(TAG, client->connection_info.query, return ESP_ERR_NO_MEM);
  102. } else if (client->connection_info.query) {
  103. free(client->connection_info.query);
  104. client->connection_info.query = NULL;
  105. }
  106. if(client->connection_info.query != NULL){
  107. printf("query:%s\n",client->connection_info.query);
  108. }else{
  109. printf("query:%s\n","NULL");
  110. }
  111. return ESP_OK;
  112. }

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,解析参数

  1. esp_err_t esp_http_client_set_post_field(esp_http_client_handle_t client, const char *data, int len)
  2. {
  3. esp_err_t err = ESP_OK;
  4. client->post_data = (char *)data;
  5. client->post_len = len;
  6. ESP_LOGD(TAG, "set post file length = %d", len);
  7. if (client->post_data) {
  8. err = esp_http_client_set_header(client, "Content-Type", "application/json");
  9. // err = esp_http_client_set_header(client, "Content-Type", "application/x-www-form-urlencoded");
  10. } else {
  11. client->post_len = 0;
  12. err = esp_http_client_set_header(client, "Content-Type", NULL);
  13. }
  14. return err;
  15. }

这个函数首先设置了HTTP请求的数据body,这个数据是第二步得到的JSON数据,然后下面设置了Content-Type内容编码,我们这里是cJSON,所以设置为application/json,注释掉原来的了。

7、esp_http_client_perform(client); 这个函数是重点,完成TCP发送和接收,主要说下三个部分

  1. esp_err_t esp_http_client_perform(esp_http_client_handle_t client)
  2. {
  3. esp_err_t err;
  4. do { //第一部分
  5. if ((err = esp_http_client_open(client, client->post_len)) != ESP_OK) {
  6. return err;
  7. }
  8. if (client->post_data && client->post_len) {
  9. printf("\r\nclient_open post_data len:%d %s\r\n\n",client->post_len,client->post_data);
  10. //第二部分
  11. if (esp_http_client_write(client, client->post_data, client->post_len) <= 0) {
  12. ESP_LOGE(TAG, "Error upload data");
  13. return ESP_ERR_HTTP_WRITE_DATA;
  14. }
  15. }
  16. 第三大部分
  17. int lenth = esp_http_client_fetch_headers(client);
  18. if (lenth < 0) {
  19. return ESP_ERR_HTTP_FETCH_HEADER;
  20. }
  21. printf("\n 12recv Data len:%d %s\n\n",client->response->buffer->len,client->response->buffer->data);
  22. if ((err = esp_http_check_response(client)) != ESP_OK) {
  23. ESP_LOGE(TAG, "Error response");
  24. return err;
  25. }
  26. while (client->response->is_chunked && !client->is_chunk_complete) {
  27. if (esp_http_client_get_data(client) <= 0) {
  28. ESP_LOGD(TAG, "Read finish or server requests close");
  29. break;
  30. }
  31. }
  32. while (client->response->data_process < client->response->content_length) {
  33. if (esp_http_client_get_data(client) <= 0) {
  34. ESP_LOGD(TAG, "Read finish or server requests close");
  35. break;
  36. }
  37. }
  38. http_dispatch_event(client, HTTP_EVENT_ON_FINISH, NULL, 0);
  39. if (!http_should_keep_alive(client->parser)) {
  40. ESP_LOGD(TAG, "Close connection");
  41. esp_http_client_close(client);
  42. } else {
  43. if (client->state > HTTP_STATE_CONNECTED) {
  44. client->state = HTTP_STATE_CONNECTED;
  45. }
  46. }
  47. } while (client->process_again);
  48. return ESP_OK;
  49. }

a、esp_http_client_open(client, client->post_len)) 函数

作用:连接TCP,并发送HTTP头

  1. esp_err_t esp_http_client_open(esp_http_client_handle_t client, int write_len)
  2. {
  3. esp_err_t err;
  4. if (client->state == HTTP_STATE_UNINIT) {
  5. ESP_LOGE(TAG, "Client has not been initialized");
  6. return ESP_ERR_INVALID_STATE;
  7. }
  8. if ((err = esp_http_client_prepare(client)) != ESP_OK) {
  9. ESP_LOGE(TAG, "Failed to initialize request data");
  10. esp_http_client_close(client);
  11. return err;
  12. }
  13. if (client->state < HTTP_STATE_CONNECTED) {
  14. ESP_LOGD(TAG, "Begin connect to: %s://%s:%d", client->connection_info.scheme, client->connection_info.host, client->connection_info.port);
  15. client->transport = transport_list_get_transport(client->transport_list, client->connection_info.scheme);
  16. if (client->transport == NULL) {
  17. ESP_LOGE(TAG, "No transport found");
  18. return ESP_ERR_HTTP_INVALID_TRANSPORT;
  19. }
  20. //第一部分连接TCP
  21. if (transport_connect(client->transport, client->connection_info.host, client->connection_info.port, client->timeout_ms) < 0) {
  22. ESP_LOGE(TAG, "Connection failed, sock < 0");
  23. return ESP_ERR_HTTP_CONNECT;
  24. }
  25. http_dispatch_event(client, HTTP_EVENT_ON_CONNECTED, NULL, 0);//产生一个连接成功事件
  26. client->state = HTTP_STATE_CONNECTED;
  27. }
  28. if (write_len >= 0) {
  29. http_header_set_format(client->request->headers, "Content-Length", "%d", write_len);
  30. } else if (write_len < 0) {
  31. esp_http_client_set_header(client, "Transfer-Encoding", "chunked");
  32. esp_http_client_set_method(client, HTTP_METHOD_POST);
  33. }
  34. int header_index = 0;
  35. int wlen = client->buffer_size;
  36. const char *method = HTTP_METHOD_MAPPING[client->connection_info.method];
  37. int first_line = snprintf(client->request->buffer->data,
  38. client->buffer_size, "%s %s",
  39. method,
  40. client->connection_info.path);
  41. if (first_line > client->buffer_size) {
  42. ESP_LOGE(TAG, "Out of buffer");
  43. return ESP_ERR_HTTP_CONNECT;
  44. }
  45. printf("\n---->:%s\n",client->request->buffer->data);
  46. if (client->connection_info.query) {
  47. first_line += snprintf(client->request->buffer->data + first_line,
  48. client->buffer_size - first_line, "?%s", client->connection_info.query);
  49. if (first_line > client->buffer_size) {
  50. ESP_LOGE(TAG, "Out of buffer");
  51. return ESP_ERR_HTTP_CONNECT;
  52. }
  53. }
  54. printf("\n---->:%s\n",client->request->buffer->data);
  55. first_line += snprintf(client->request->buffer->data + first_line,
  56. client->buffer_size - first_line, " %s\r\n", DEFAULT_HTTP_PROTOCOL);
  57. if (first_line > client->buffer_size) {
  58. ESP_LOGE(TAG, "Out of buffer");
  59. return ESP_ERR_HTTP_CONNECT;
  60. }
  61. wlen -= first_line;
  62. while ((header_index = http_header_generate_string(client->request->headers, header_index, client->request->buffer->data + first_line, &wlen))) {
  63. if (wlen <= 0) {
  64. break;
  65. }
  66. if (first_line) {
  67. wlen += first_line;
  68. first_line = 0;
  69. }
  70. client->request->buffer->data[wlen] = 0;
  71. ESP_LOGD(TAG, "Write header[%d]: %s", header_index, client->request->buffer->data);
  72. printf("\r\nclient_open Write Data len:%d %s\r\n\n",wlen,client->request->buffer->data);
  73. //第二部分 发送http头
  74. if (transport_write(client->transport, client->request->buffer->data, wlen, client->timeout_ms) <= 0) {
  75. ESP_LOGE(TAG, "Error write request");
  76. esp_http_client_close(client);
  77. return ESP_ERR_HTTP_WRITE_DATA;
  78. }
  79. wlen = client->buffer_size;
  80. }
  81. client->state = HTTP_STATE_REQ_COMPLETE_HEADER;
  82. return ESP_OK;
  83. }

函数源码比较长,这个里面主要可以分成两部分,其它基本是组包,组成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等待接收数据,

  1. int esp_http_client_fetch_headers(esp_http_client_handle_t client)
  2. {
  3. if (client->state < HTTP_STATE_REQ_COMPLETE_HEADER) {
  4. return ESP_FAIL;
  5. }
  6. client->state = HTTP_STATE_REQ_COMPLETE_DATA;
  7. esp_http_buffer_t *buffer = client->response->buffer;
  8. client->response->status_code = -1;
  9. while (client->state < HTTP_STATE_RES_COMPLETE_HEADER) {
  10. buffer->len = transport_read(client->transport, buffer->data, client->buffer_size, client->timeout_ms);
  11. if (buffer->len <= 0) {
  12. return ESP_FAIL;
  13. }
  14. http_parser_execute(client->parser, client->parser_settings, buffer->data, buffer->len);
  15. }
  16. buffer->data[buffer->len] = 0;
  17. ESP_LOGD(TAG, "content_length = %d", client->response->content_length);
  18. if (client->response->content_length <= 0) {
  19. client->response->is_chunked = true;
  20. return 0;
  21. }
  22. return client->response->content_length;
  23. }

这个函数比较简单,就是在里面一直等待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

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Li_阴宅/article/detail/875983
推荐阅读
相关标签
  

闽ICP备14008679号