第三章 设置DNS、静态IP实现域名解析
【ESP32-S3的开发】前言一、关闭DHCP,设置DNS、网关、子网掩码、静态IP
1.关闭DHCP2.设置 IP 的主要函数3.设置 DNS3.设置 网关、子网掩码、静态IP 二、域名解析API
1.getaddrinfo2.完整的示例代码 总结
前言
其实这个和 ESP 系列关系不大了,目前 ESP32 大部分都支持,我也就把文章定位在这个系列上吧!
在讲述ESP32S3设置DNS、静态IP,实现域名解析前,先了解一下域名解析的相关概念
通俗理解~主机名、IP、域名、端口、DNS服务器
https://www.cnblogs.com/juggdxy/p/7193598.html
DNS解析的工作原理
https://blog.csdn.net/zhengqijun_/article/details/53811229
子网掩码和ip地址的关系(理解如何通过子网掩码和 IP 地址得到网络地址和主机名)
https://www.html.cn/qa/other/21879.html
DHCP概念
https://blog.csdn.net/m0_37836194/article/details/98721097
可能会有人提出这么个疑问,DHCP自动获得服务器分配的lP地址和子网掩码不就可以了吗?为毛要设置另外的DNS、网关和静态IP,甚至是端口?
这里面介绍一种场景:公司的主机使用运营商的DNS,直接连接外网,但公司因为需要限制某部分IP的访问,对访问外网的出口做了部分限制,这时候设置了公司自己的DNS,其他主机通过公司的服务器连接外网时,就都能被管控了。在一些信息管控比较严格的地方会有这种情况出现,如果设备需要部署在这些地方时,对方只开放部分静态IP给设备,用以连接,从而让设备接受监管,这时候就需要设备设置DNS、静态IP等操作了
一、关闭DHCP,设置DNS、网关、子网掩码、静态IP
需要添加的头文件
#include "lwip/dns.h" #include "lwip/err.h" #include "lwip/netdb.h" #include "lwip/sys.h"1.关闭DHCP
tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); //在 Station 接口上停止 DHCP 客户端2.设置 IP 的主要函数
IP4_ADDR(ipaddr, a,b,c,d)
为了简便解析以及配置,这边封装一个解析函数,匹配所有和 IP 设置相关的操作, 用户习惯性传入诸如:192.168.1.1 这种字符格式的参数,这里根据这种格式,解析并进行设置,当然,最终启用并且生效的函数仍然是上面讲到的 IP4_ADDR
void SetDNS_GW_STATICIP(ip4_addr_t *ip4_usr_addr, char *ip4_str)
{
char iptemp[4];
uint8_t ip[4];
char *ptemp1 = ip4_str, *ptemp2;
uint8_t i = 0, comma_num = 0;
while (ip4_str[i] != ' ')
{
if (ip4_str[i] == '.')
{
comma_num++;
}
i++;
}
if (comma_num == 3)
{
for (i = 0; i < comma_num; i++)
{
ptemp2 = ptemp1;
ptemp1 = strchr(ptemp1, '.');
memset(iptemp, 0, sizeof(iptemp));
strncpy(iptemp, ptemp2, strlen(ptemp2) - strlen(ptemp1));
ip[i] = atoi(iptemp);
ptemp1 += 1;
}
memset(iptemp, 0, sizeof(iptemp));
strncpy(iptemp, ptemp1, strlen(ptemp1));
ip[i] = atoi(iptemp);
IP4_ADDR(ip4_usr_addr, ip[0], ip[1], ip[2], ip[3]);
}
}
3.设置 DNS
typedef struct __dns_gw_staticip
{
char dns_ipaddr[16];
char gw_ipaddr[16];
char staticip_ipaddr[16];
char netmask_ipaddr[16];
} dns_gw_static_st;
dns_gw_static_st NetRec_NetAddrConfigData = {0};
memset(NetRec_NetAddrConfigData.dns_ipaddr, 0, sizeof(NetRec_NetAddrConfigData.dns_ipaddr));
memcpy(NetRec_NetAddrConfigData.dns_ipaddr, "192.168.100.16", strlen("192.168.100.16"));
tcpip_adapter_dns_info_t dns_info = {0};
SetDNS_GW_STATICIP((ip4_addr_t *)(&dns_info.ip), NetRec_NetAddrConfigData.dns_ipaddr);
ESP_ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_DNS_FALLBACK, &dns_info));
上面用到的 tcpip_adapter_dns_info_t 结构体来源于 esp-idf/components/tcpid_adapter/include 路径下的头文件
3.设置 网关、子网掩码、静态IPtcpip_adapter_ip_info_t ip_info = {0};
//具体IP、掩码、网关是多少大家自己视情况而定
SetDNS_GW_STATICIP(&ip_info.ip, NetRec_NetAddrConfigData.staticip_ipaddr);
SetDNS_GW_STATICIP(&ip_info.gw, NetRec_NetAddrConfigData.gw_ipaddr);
SetDNS_GW_STATICIP(&ip_info.netmask, NetRec_NetAddrConfigData.netmask_ipaddr);
ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info));
二、域名解析API
1.getaddrinfo
指定域名、端口号,根据指定返回协议的协议簇、返回地址的类型,创建出域名解析的结果,并被 result 指向,可以通过 result 协议簇解析得到 域名对应的 IP 地址和端口号
int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );2.完整的示例代码
#include "esp_event_loop.h" #include "esp_log.h" #include "esp_system.h" #include "esp_wifi.h" #include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "freertos/task.h" #include "nvs_flash.h" #include#include "lwip/dns.h" #include "lwip/err.h" #include "lwip/ip_addr.h" #include "lwip/netdb.h" #include "lwip/sys.h" #define ENABLE_STATIC_IP 1 #define EXAMPLE_ESP_WIFI_SSID CONFIG_ESP_WIFI_SSID #define EXAMPLE_ESP_WIFI_PASS CONFIG_ESP_WIFI_PASSWORD #define EXAMPLE_ESP_MAXIMUM_RETRY CONFIG_ESP_MAXIMUM_RETRY static EventGroupHandle_t s_wifi_event_group; const int WIFI_CONNECTED_BIT = BIT0; static const char *TAG = "wifi station"; static int s_retry_num = 0; const struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_STREAM, }; uint32_t ipv4_cnt; int error; ip_addr_t getIP = {0}; bool bDNSFound = 0; 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: ESP_LOGI(TAG, "got ip:%s", ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); s_retry_num = 0; xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); break; case SYSTEM_EVENT_STA_DISCONNECTED: { if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { esp_wifi_connect(); xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT); s_retry_num++; ESP_LOGI(TAG, "retry to connect to the AP"); } ESP_LOGI(TAG, "connect to the AP failn"); break; } default: break; } return ESP_OK; } void wifi_init_sta() { s_wifi_event_group = xEventGroupCreate(); tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); wifi_config_t wifi_config = { .sta = {.ssid = EXAMPLE_ESP_WIFI_SSID, .password = EXAMPLE_ESP_WIFI_PASS}, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); #if ENABLE_STATIC_IP tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA); tcpip_adapter_dns_info_t dns_info = {0}; tcpip_adapter_ip_info_t ip_info = {0}; //可以用手机开的热点来测试 IP4_ADDR(&ip_info.ip, 172, 20, 10, 3); IP4_ADDR(&ip_info.gw, 172, 20, 10, 1); IP4_ADDR(&ip_info.netmask, 255, 255, 255, 240); IP_ADDR4(&dns_info.ip, 172, 20, 10, 1); ESP_ERROR_CHECK(tcpip_adapter_set_dns_info(TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_DNS_FALLBACK, &dns_info)); ESP_ERROR_CHECK(tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info)); #else #endif ESP_ERROR_CHECK(esp_wifi_start()); ESP_LOGI(TAG, "wifi_init_sta finished."); ESP_LOGI(TAG, "connect to ap SSID:%s password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); } void dns_found_cb(const char *name, const ip_addr_t *ipaddr, void *callback_arg) { getIP = *ipaddr; ESP_LOGE(TAG, "baidu.com ip is = %d", getIP.u_addr.ip4.addr); ESP_LOGI(TAG, "internet connection : %s and ip : %d", error ? "false" : "true", getIP.u_addr.ip4.addr); } void app_main() { // Initialize NVS 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); ESP_LOGI(TAG, "ESP_WIFI_MODE_STA"); wifi_init_sta(); EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); if (bits & WIFI_CONNECTED_BIT) { printf("Ready to Anasis ~rn"); struct addrinfo *result; int err; err = getaddrinfo("www.baidu.com", "80", &hints, &result); if (err != 0) printf("getaddrinfo err: %d n", err); // 3、将获取到的信息打印出来 char buf[100]; struct sockaddr_in *ipv4 = NULL; 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"); // 4、释放addrinfo 内存 freeaddrinfo(result); } }
总结
代码改编至示例,上面的 API 也是自己瞎封装,但也能实现功能,可能有其他更好的方法,有空一起探讨



