ESP32开发之路(8)—ESP32通过DNS连接到百度

ESP32开发之路(8)—ESP32通过DNS连接到百度

本次开发环境是在Ubuntu下的,使用的模块是GOOUUU-ESP32,在Win10下使用VSCode远程编辑项目。代码使用来自esp-idf的例程。

一、准备

可以先看一下:C语言网络编程(4)— 通过DNS连接到百度-优化在Win10上使用SSH远程连接Linux搭建VSCode开发环境
在上个工程(ESP32开发之路(7)—ESP32作为TCP客户端连接到局域网的PC机)的基础上,将tcp_client_task任务重写为dns_client_task。

二、DNS解析

在esp-idf里的getaddrinfo()好像是被阉割了,有一些功能是没有被实现的,毕竟ESP32只是一个MCU而已。
经过的我的测试,发现解析后对于有多个IP地址的域名也只能得到一个IP地址,而且第二个参数好像不支持服务名称,如"ftp"、"http"等,只支持端口号字串,不过如果域名有IPv6地址,将ai_family设置为AF_INET6也能查到Iv6地址。
首先,添加我们需要用到的头文件

#include "lwip/dns.h"

定义一个hints结构体,用来设置函数的getaddrinfo()的使用方式:

    // 1、定义一个hints结构体,用来设置函数的getaddrinfo()的使用方式
    const struct addrinfo hints = {
        .ai_family = AF_INET,           /* 指定返回地址的协议簇,AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNSPEC(IPv4 and IPv6)*/
        .ai_socktype = SOCK_STREAM,     /* 设定返回地址的socket类型,流式套接字 */     
    };

使用getaddrinfo()开始解析了,注意,第二个参数要十进制的端口号(“8080”)字符串

	// 2、使用getaddrinfo()开始解析,定义一个struct addrinfo结构体指针,用来获取解析结果
    struct addrinfo *result;
    int err;
    err = getaddrinfo("www.163.com", "80", &hints, &result);
    if(err != 0)        /* 返回值不为0,函数执行失败*/
        printf("getaddrinfo err: %d \n",err);

将获取到的信息打印出来

	// 3、将获取到的信息打印出来
    char buf[100];                      /* 用来存储IP地址字符串 */
    struct sockaddr_in  *ipv4 = NULL;   /* IPv4地址结构体指针 */
    if(result->ai_family == AF_INET) 
    {
        ipv4 = (struct sockaddr_in *)result->ai_addr;
        inet_ntop(result->ai_family, &ipv4->sin_addr, buf, sizeof(buf));
        printf("[IPv4-%d]%s [port]%d \n",ipv4_cnt,buf,ntohs(ipv4->sin_port));
    }
    else
        printf("got IPv4 err !!!\n");

使用完我们需要释放addrinfo 内存

    // 4、释放addrinfo 内存
    freeaddrinfo(result);     

编译,烧录,运行成功
在这里插入图片描述
我们尝试将域名换成“www.163.com”,即网易的域名,然后ai_family都换成AF_INET6,可以看到,获取到了IPv6地址
在这里插入图片描述

三、连接百度服务器

尝试连接到百度服务器

	// 4、使用socket()函数获取一个TCP客户端socket文件描述符
	int tcp_client = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == tcp_client)
	{
		perror("socket");
		return ;
	}

    // 5、链接到服务器
    err = connect(tcp_client, (const struct sockaddr *)ipv4, sizeof(*ipv4));
    if (err < 0)
		perror("connect err");
    else
	    printf("connect success, ret = %d.\n", err);

编译,烧录,可以看到,连接成功
在这里插入图片描述
我们尝试发送一个GET请求,

    // 6. 发送GET请求
	char sendbuf[]={"GET / HTTP/1.1\n\n"};
	err = send(tcp_client, sendbuf, strlen(sendbuf),0);
    if (err < 0)
		perror("send err");
    else
        printf("send size %d \n",err);
    

    // 7、等待接收服务端发送过来的数据,最大接收100个字节
    char recvbuf[100] = {0};
    err = recv(tcp_client, recvbuf, sizeof(recvbuf), 0);
    if (err < 0)
		perror("recv err");
    else
        printf("recv size %d \n",err);

    // 8、将接收到的数据打印出来
    printf("Recvdate: \n%s \n",recvbuf);

    // 9、关闭套接字
    close(tcp_client);

可以看到,请求成功
在这里插入图片描述

五、代码

最后贴上app_main.c完整代码

#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "driver/gpio.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"

#define GPIO_LED_NUM 2                      /* LED引脚编号 */

/* wifi连接函数,位于app_wifi.c,阻塞状态 */
void wifi_connect_init(void);

#define HOST_IP_ADDR "192.168.0.102"

static void dns_client_task(void *pvParameters)
{
    // 1、定义一个hints结构体,用来设置函数的getaddrinfo()的使用方式
    const struct addrinfo hints = {
        .ai_family = AF_INET,           /* 指定返回地址的协议簇,AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNSPEC(IPv4 and IPv6)*/
        .ai_socktype = SOCK_STREAM,     /* 设定返回地址的socket类型,流式套接字 */     
    };

    // 2、使用getaddrinfo()开始解析,定义一个struct addrinfo结构体指针,用来获取解析结果
    struct addrinfo *result;
    int err;
    err = getaddrinfo("www.baidu.com", "80", &hints, &result);
    if(err != 0)        /* 返回值不为0,函数执行失败*/
        printf("getaddrinfo err: %d \n",err);

	// 3、将获取到的信息打印出来
    char buf[100];                      /* 用来存储IP地址字符串 */
    struct sockaddr_in  *ipv4 = NULL;   /* IPv4地址结构体指针 */
    if(result->ai_family == AF_INET) 
    {
        ipv4 = (struct sockaddr_in *)result->ai_addr;
        inet_ntop(result->ai_family, &ipv4->sin_addr, buf, sizeof(buf));
        printf("[IPv4]%s [port]%d \n",buf,ntohs(ipv4->sin_port));
    }
    else
        printf("got IPv4 err !!!\n");

    // 4、使用socket()函数获取一个TCP客户端socket文件描述符
	int tcp_client = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == tcp_client)
	{
		perror("socket");
		return ;
	}

    // 5、链接到服务器
    err = connect(tcp_client, (const struct sockaddr *)ipv4, sizeof(*ipv4));
    if (err < 0)
		perror("connect err");
    else
	    printf("connect success, ret = %d.\n", err);
    
    // 6. 发送GET请求
	char sendbuf[]={"GET / HTTP/1.1\n\n"};
	err = send(tcp_client, sendbuf, strlen(sendbuf),0);
    if (err < 0)
		perror("send err");
    else
        printf("send size %d \n",err);
    

    // 7、等待接收服务端发送过来的数据,最大接收100个字节
    char recvbuf[100] = {0};
    err = recv(tcp_client, recvbuf, sizeof(recvbuf), 0);
    if (err < 0)
		perror("recv err");
    else
        printf("recv size %d \n",err);

    // 8、将接收到的数据打印出来
    printf("Recvdate: \n%s \n",recvbuf);

    // 9、关闭套接字
    close(tcp_client);

    // 10、释放addrinfo 内存
    freeaddrinfo(result);     

    vTaskDelete(NULL);
}


void app_main(void)
{
    /* 打印Hello world! */
    printf("Hello world!\n");

    wifi_connect_init();

    xTaskCreate(dns_client_task, "tcp_client", 4096, NULL, 5, NULL);

    while(1)
    {
        gpio_set_level(GPIO_LED_NUM, 0);        /* 熄灭 */
        vTaskDelay(500 / portTICK_PERIOD_MS);   /* 延时500ms*/
        gpio_set_level(GPIO_LED_NUM, 1);        /* 点亮 */
        vTaskDelay(500 / portTICK_PERIOD_MS);   /* 延时500ms*/
    }
}

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值