B站Michale_ee——ESP32_IDF SDK——WiFi_1 AP、STA模式、TCP、UDP

发布于:2025-05-09 ⋅ 阅读:(24) ⋅ 点赞:(0)

一、开篇介绍——What?讲什么

  • Data Communication 数据通信;
    • WiFi的最终目的是进行数据通信;
      在这里插入图片描述
      在这里插入图片描述

二、WiFi 接入点模式工作流程(Access Point)

在这里插入图片描述

1.WiFi代码的层次划分

在这里插入图片描述

2.官方示例分析

在这里插入图片描述

/*  WiFi softAP Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sys.h"

/* The examples use WiFi configuration that you can set via project configuration menu.

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_WIFI_CHANNEL   CONFIG_ESP_WIFI_CHANNEL
#define EXAMPLE_MAX_STA_CONN       CONFIG_ESP_MAX_STA_CONN

static const char *TAG = "wifi softAP";

static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                                    int32_t event_id, void* event_data)
{
    if (event_id == WIFI_EVENT_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
                 MAC2STR(event->mac), event->aid);
    } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
                 MAC2STR(event->mac), event->aid);
    }
}

void wifi_init_softap(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_ap();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}

void app_main(void)
{
    //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_AP");
    wifi_init_softap();
}

(0)初始化存储空间,NVS,“Non-Volatile Storage”,即非易失性存储

在这里插入图片描述

(1)初始化底层的TCP/IP协议栈—>LWIP Task

在这里插入图片描述

(2)创建默认的事件循环—>event Task

在这里插入图片描述

(3)创建默认的WiFi热点

在这里插入图片描述

(4)初始化WiFi,注册WiFi-AP事件处理程序、设置WiFi的工作模式、配置WiFi、启动WiFi

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

3.示例代码运行及结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、WiFi 站模式工作流程(Station)

在这里插入图片描述

1.官方示例分析

在这里插入图片描述

/* WiFi station Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.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 "lwip/err.h"
#include "lwip/sys.h"

/* The examples use WiFi configuration that you can set via project configuration menu

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#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

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static const char *TAG = "wifi station";

static int s_retry_num = 0;

static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } else {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void wifi_init_sta(void)
{
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
	     .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    /* The event will not be processed after unregister */
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    vEventGroupDelete(s_wifi_event_group);
}

void app_main(void)
{
    //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();
}

(0)初始化存储空间,NVS,“Non-Volatile Storage”,即非易失性存储

在这里插入图片描述

(1)初始化底层的TCP/IP协议栈—>LWIP TASK

在这里插入图片描述

(2)创建默认的事件循环—>event task

在这里插入图片描述

(3)创建默认的WiFi站点

在这里插入图片描述

(4)初始化WiFi,注册WiFi-AP事件处理程序、设置WiFi的工作模式、配置WiFi、启动WiFi

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.示例代码运行及结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、基于LWIP(Light Weight)的TCP客户端

ESP32 编程指南 LWIP简介
在这里插入图片描述
在这里插入图片描述

1.socket简介

(1)socket的定义

  • socket,套接字,是一种编程接口,提供了一种让不同设备上的进程能够通过网络进行通信的机制。
    可以看作不同计算机之间进行数据交换的通道,借助这个接口,程序能够在网络上发送和接收数据。

(2)socket的工作原理

在这里插入图片描述

  • socket基于客户端-服务器(client - server)模型,其基本工作流程如下:
  • 服务器端:
    • 创建套接字:调用socket()函数创建一个套接字对象;
    • 绑定地址:使用bind()函数将套接字与特定的IP地址和端口绑定;
    • 监听连接:通过listen()函数开始监听客户端的连接请求;
    • 接受连接:当有客户端发起请求时,使用accept()函数接受连接,并返回一个新的套接字对象用于与客户端进行通信;
    • 数据收发:使用新的套接字对象进行数据的发送和接收;
    • 关闭连接:通信结束后,关闭套接字;
  • 客户端:
    • 创建套接字:调用socket()函数创建一个套接字对象;
    • 连接服务器:使用connect()函数向服务器的IP地址和端口号发起连接请求;
    • 数据收发:连接成功后,使用套接字进行数据的发送和接收;
    • 关闭连接:通信结束后,关闭套接字;

(3)socket的分类

  • 根据使用的传输层协议不同,socket 主要分为以下两类:
    • TCP 套接字:基于 TCP(传输控制协议)实现,提供面向连接的、可靠的、基于字节流的通信服务。在进行数据传输之前,需要先建立连接,传输过程中保证数据的顺序和完整性,适用于对数据准确性要求较高的场景,如文件传输、网页浏览等。
    • UDP 套接字:基于 UDP(用户数据报协议)实现,提供无连接的、不可靠的、基于数据报的通信服务。不需要建立连接,数据以独立的数据报形式发送,不保证数据的顺序和完整性,但具有较低的延迟,适用于对实时性要求较高的场景,如视频会议、在线游戏等。

(4)socket的应用场景

  • socket 在网络编程中有着广泛的应用,常见的应用场景包括:
    • 网络服务:如 Web 服务器、邮件服务器、FTP 服务器等,通过 socket 与客户端进行通信,提供各种网络服务。
    • 即时通讯:如 QQ、微信等即时通讯软件,利用 socket 实现消息的实时发送和接收。
    • 远程控制:通过 socket 实现对远程设备的控制和管理,如远程桌面、智能家居控制等。
    • 分布式系统:在分布式系统中,不同节点之间通过 socket 进行数据交换和协调工作。

(5)部分socket API详解

【1】socket()
  • 函数原型:
    int socket(int domain, int type, int protocol);
    
  • 作用:创建一个套接字,它是网络通信的基础,后续的网络操作都基于此套接字进行;
  • 参数:
    • domain:指定协议族,决定了使用的网络层协议,常见取值有:
      • AF_INET:使用IPv4协议;
      • AF_INET6:使用IPv6协议;
      • AF_UNIX:用于本地进程间通信;
    • type:指定套接字的类型,决定了传输层协议和通信方式,常见的取值有:
      • SOCK_STREAM:面向来连接、可靠的流式传输,通常对应TCP协议;
      • SOCK_DGRAM:无连接,不可靠的数据报传输,通常对应UDP协议;
      • SOCK_RAW:允许直接访问底层网络协议,用于开发网络诊断工具等;
    • protocol:指定具体使用的协议; 通常设为0,表示使用对应的domain和type的默认协议。例如,SOCK_STREAM时默认是TCP,SOCK_DGRAM时默认是UDP;
  • 返回值:
    • 成功,返回一个非负整数的套接字描述符;
    • 失败,返回**-1**,并设置errno为相应的错误码,如EAFNOSUPPORT(不支持指定的协议族);
【2】setsockopt()
  • 函数原型:
    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
    
  • 作用:设置套接字的选项值;
  • 参数:
    • sockfd:要操作的套接字描述符;
    • level:指定选项所在的协议层;
      • 常见取值:
        SOL_SOCKET:表示通用的套接字选项,这些选项不特定于某个协议;
        IPPROTO_IP:用于设置 IPv4 相关的选项;
        IPPROTO_IPV6:用于设置 IPv6 相关的选项;
        IPPROTO_TCP:用于设置 TCP 相关的选项;
        IPPROTO_UDP:用于设置 UDP 相关的选项;
    • optname:指定要设置的选项名称,取值依赖于 level 参数;
      • 当 level 为 SOL_SOCKET 时
        • SO_REUSEADDR:允许在同一端口上重新绑定套接字,常用于服务器程序,避免在服务器重启时因端口被占用而无法绑定。其 optval 为一个整数,非零值表示启用,零值表示禁用;
        • SO_KEEPALIVE:开启套接字的保活机制,定期发送探测包以检测连接是否仍然有效。optval 为非零整数表示启用,零表示禁用
      • 当 level 为 IPPROTO_TCP 时
        • TCP_KEEPIDLE:指定了在 TCP 连接处于空闲状态(即没有数据传输)多长时间后,才开始发送第一个保活探测包;该选项的值是一个以秒为单位的整数;
        • TCP_KEEPINTVL:规定了在发送第一个保活探测包之后,后续保活探测包之间的发送间隔时间。也就是说,当发送了第一个保活探测包后,如果没有收到对方的响应,那么每隔 TCP_KEEPINTVL 秒就会再发送一个探测包;该选项的值是一个以秒为单位的整数;
        • TCP_KEEPCNT:它表示在判定 TCP 连接失效之前,最多允许发送多少个连续的保活探测包且都没有收到对方的响应。如果发送的探测包数量达到了 TCP_KEEPCNT 个仍未收到响应,就会认为连接已经断开;
    • optval:指向包含要设置的选项值的缓冲区;
    • optlen:选项值的长度;
  • 返回值:
    • 成功:返回 0;
    • 失败:返回 -1,并设置 errno 为相应的错误码;
【3】getsockopt()
  • 函数原型
    int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
    
  • 作用:获取套接字的选项值;
  • 参数:
    • sockfd:要操作的套接字描述符;
    • level:指定选项所在的协议层,常见取值有 SOL_SOCKET(通用套接字选项)、IPPROTO_TCP(TCP 选项)等;
    • optname:指定要获取的选项名称,如 SO_REUSEADDR(允许地址重用);
    • optval:指向用于存储选项值的缓冲区;
    • optlen:指向 socklen_t 类型的指针,用于存储选项值的长度。调用前需设置为 optval 所指向缓冲区的大小;
  • 返回值:
    • 成功:返回 0;
    • 失败:返回 -1,并设置 errno 为相应的错误码;
【4】bind()
  • 函数原型
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  • 作用:将一个套接字绑定到指定的IP地址和端口号,使得该套接字可以在指定的地址和端口上通信;
  • 参数:
    • sockfd:socket()函数返回的套接字描述符;
    • addr:指向struct sockaddr类型的指针,存储要绑定的地址信息,实际使用中尝试用struct sockaddr_in(IPv4)或struct sock_addr_in6(IPv6),使用时需进行强制类型转换;
    • addrlen:地址结构的长度,可通过 sizeof(struct sockaddr_in) 或 sizeof(struct sockaddr_in6) 获取
  • 返回值:
    • 成功:返回 0;
    • 失败:返回 -1,并设置 errno 为相应的错误码,如 EADDRINUSE(地址已被使用);
  • INADDR_ANY通常被定义为 0,在 IPv4 网络编程中,它表示主机上的所有网络接口。
    • 当一个服务器程序绑定到INADDR_ANY时,它会监听所有可用的网络接口,接受来自任何 IP 地址的连接请求。这样,服务器就可以通过主机上的任何网络接口来接收网络数据,而不必指定具体的 IP 地址;
    • 在编写网络服务器程序时,通常会将服务器绑定到INADDR_ANY。这样,无论服务器所在的主机有多少个网络接口,或者其 IP 地址如何变化,服务器都能够接收到发送给它的网络数据包;
【5】listen()
  • 函数原型
    int listen(int sockfd, int backlog);
    
  • 作用:listen 函数的主要作用是把一个套接字设定为监听状态,让它能够接收客户端的连接请求。此函数一般在服务器端调用,并且是在 socket 函数创建套接字、bind 函数将套接字绑定到特定地址和端口之后使用;
  • 参数:
    • sockfd:这是一个文件描述符,代表由 socket 函数创建的套接字。该套接字应当是流式套接字(SOCK_STREAM),通常用于 TCP 连接;
    • backlog:这是一个整数,用于指定允许在队列中等待处理的最大连接请求数。当有多个客户端同时发起连接请求时,服务器无法立刻处理所有请求,这些未处理的请求会被放入一个队列中等待处理。backlog 就是这个队列的最大长度。如果队列已满,新的连接请求会被拒绝。不同系统对 backlog 的最大值有不同限制,一般可以设置为 5 或 10。
  • 返回值:
    • 成功:返回 0;
    • 失败:返回 -1,并且会设置 errno 来指示具体的错误原因,常见的错误码如下:
      EBADF:sockfd 不是一个有效的文件描述符。
      EOPNOTSUPP:sockfd 对应的套接字类型不支持监听操作(例如不是流式套接字)。
      EADDRINUSE:套接字已经处于监听状态或者绑定的地址和端口正在被其他进程使用。
【6】connect()
  • 函数原型
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  • 作用:connect 函数的主要作用是在客户端建立与服务器的连接。它允许客户端指定要连接的服务器的地址和端口,并尝试与服务器建立 TCP 连接
  • 参数:
    • sockfd:一个由 socket 函数创建的套接字描述符,表示客户端的套接字;
    • addr:一个指向 struct sockaddr 结构体的指针,包含了要连接的服务器的地址信息。在实际使用中,通常会根据使用的协议(如 IPv4 或 IPv6)将其转换为相应的结构体(如 struct sockaddr_in 或 struct sockaddr_in6);
    • addrlen:addr 结构体的长度,以字节为单位
  • 返回值:
    • 成功:返回 0,表示连接成功建立;
    • 失败:返回 -1,并设置 errno 以指示错误原因。常见的错误码包括:
      • EACCES:权限不足,可能是因为没有足够的权限访问指定的端口;
      • EADDRINUSE:指定的地址正在被使用;
      • ECONNREFUSED:连接被拒绝,通常是因为服务器没有在指定的端口上监听;
      • EINPROGRESS:套接字正在进行非阻塞连接,并且连接尚未完成;
      • ENETUNREACH:网络不可达,可能是因为网络配置问题或目标服务器不可用;
【7】accept()
  • 函数原型
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    
  • 作用:从监听队列中取出一个连接请求,并创建一个新的套接字用于与客户端进行通信。该函数主要用于 TCP 服务器端。
  • 参数:
    • sockfd:处于监听状态的套接字描述符,由 listen() 函数设置;
    • addr:指向 struct sockaddr 类型的指针,用于存储客户端的地址信息。可以为 NULL,表示不关心客户端地址;
    • addrlen:指向 socklen_t 类型的指针,用于存储客户端地址结构的长度。调用前需设置为 addr 所指向结构的大小;
  • 返回值:
    • 成功:返回一个新的套接字描述符,用于与客户端进行通信
    • 失败:返回 -1,并设置 errno 为相应的错误码,如 EBADF(无效的文件描述符);
【8】send
  • 函数原型:
    ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    
  • 作用:向已连接的套接字发送数据;
  • 参数:
    • sockfd:已连接的套接字描述符;
    • buf:指向要发送数据的缓冲区;
    • len:要发送的数据长度;
    • flags:发送标志,常见取值有:
      • 0:无特殊标志;
      • MSG_DONTROUTE:不进行路由查找,直接将数据发送到本地网络;
      • MSG_OOB:发送带外数据;
  • 返回值:
    • 成功:返回实际发送的字节数;
    • 失败:返回 -1,并设置 errno 为相应的错误码;
【9】sendto
  • 函数原型:
    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
    
  • 作用:向指定的目标地址发送数据。主要用于 UDP 套接字;
  • 参数:
    • sockfd:套接字描述符;
    • buf:指向要发送数据的缓冲区;
    • len:要发送的数据长度;
    • flags:发送标志;
    • dest_addr:指向 struct sockaddr 类型的指针,存储目标地址信息;
    • addrlen:目标地址结构的长度;
  • 返回值:
    • 成功:返回实际发送的字节数;
    • 失败:返回 -1,并设置 errno 为相应的错误码;
【10】recv
  • 函数原型:
    ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    
  • 作用:从已连接的套接字接收数据;
  • 参数:
    • sockfd:已连接的套接字描述符;
    • buf:指向用于存储接收数据的缓冲区;
    • len:缓冲区的长度;
    • flags:接收标志,常见取值有:
      • 0:无特殊标志;
      • MSG_PEEK:查看数据,但不将其从接收队列中移除;
      • MSG_OOB:接收带外数据;
        • 带外数据: 是指在正常的 TCP 数据流之外,发送方可以紧急发送给接收方的少量数据。它提供了一种让发送方能够迅速向接收方传达紧急信息的途径,这些信息需要优先于普通数据被处理。
  • 返回值:
    • 成功:返回实际接收的字节数。若返回 0,表示连接关闭
    • 失败:返回 -1,并设置 errno 为相应的错误码;
【11】recvfrom
  • 函数原型:
    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
    
  • 作用:从套接字接收数据,并获取发送方的地址信息。主要用于 UDP 套接字
  • 参数:
    • sockfd:套接字描述符;
    • buf:指向用于存储接收数据的缓冲区;
    • len:缓冲区的长度;
    • flags:接收标志;
    • src_addr:指向 struct sockaddr 类型的指针,用于存储发送方的地址信息;
    • addrlen:指向 socklen_t 类型的指针,用于存储发送方地址结构的长度。调用前需设置为 src_addr 所指向结构的大小;
  • 返回值:
    • 成功:返回实际接收的字节数;
    • 失败:返回 -1,并设置 errno 为相应的错误码;
【12】shutdown()
  • 函数原型:
    int shutdown(int sockfd, int how);
    
  • 作用:关闭套接字的部分或全部通信功能。与 close() 不同,shutdown() 可以更灵活地控制关闭的方向(读、写或两者)。
  • 参数:
    • sockfd:要操作的套接字描述符;
    • how:指定关闭的方式,常见取值有:
      SHUT_RD(0):关闭套接字的读功能,不再接收数据。
      SHUT_WR(1):关闭套接字的写功能,不再发送数据。
      SHUT_RDWR(2):同时关闭读和写功能。
  • 返回值:
    • 成功:返回 0。
    • 失败:返回 -1,并设置 errno 为相应的错误码,如 ENOTCONN(套接字未连接)。
【13】close()
  • 函数原型:
    int close(int sockfd);
    
  • 作用:关闭一个套接字,释放相关的系统资源。当不再需要使用该套接字进行通信时,应调用此函数
  • 参数:
    • sockfd:要关闭的套接字描述符;
  • 返回值:
    • 成功:返回 0;
    • 失败:返回 -1,并设置 errno 为相应的错误码,如 EBADF(无效的文件描述符);

(6)网络编程的部分常用API

  • 不同的计算机系统可能采用不同的字节序来存储数据。例如,大端字节序(Big - Endian)将数据的高位字节存于低地址,低位字节存于高地址;小端字节序(Little - Endian)则相反。为了确保网络上不同主机之间能够正确地交换和解析数据,网络协议规定了统一的网络字节序,通常采用大端字节序。因此,在将数据通过网络发送之前,需要使用 htonl 和 htons 等函数将主机字节序转换为网络字节序;在接收数据后,又需要使用相应的函数(如 ntohl 和 ntohs)将网络字节序转换为主机字节序,以便在本地进行正确的处理和存储。
【1】htonl
  • 函数原型:
    uint32_t htonl(uint32_t hostlong);
    
  • 作用:将主机字节序的长整型数据转换为网络字节序
  • 参数:hostlong 是要转换的主机字节序的长整型数据;
  • 返回值:返回转换后的网络字节序的长整型数据;
【2】htons
  • 函数原型:
    uint16_t htons(uint16_t hostshort);
    
  • 作用:将主机字节序的短整型数据转换为网络字节序。与 htonl 函数类似,htons 函数专门用于短整型数据(通常是 16 位)的字节序转换;
  • 参数:hostshort 是要转换的主机字节序的短整型数据;
  • 返回值:返回转换后的网络字节序的短整型数据;
【3】inet_addr
  • 函数原型:
    in_addr_t inet_addr(const char *cp);
    
  • 作用:将点分十进制表示的 IPv4 地址转换为 32 位的二进制网络字节序整数
  • 参数:这是一个指向以点分十进制表示的 IPv4 地址字符串的指针,例如 “192.168.1.1”
  • 返回值:
    • 成功时:返回一个 32 位的二进制网络字节序整数,代表转换后的 IPv4 地址;
    • 失败时:返回 INADDR_NONE(通常为 0xFFFFFFFF),这意味着输入的字符串不是一个有效的 IPv4 地址;
【4】inet_ntoa_r
  • 函数原型:
    char *inet_ntoa_r(struct in_addr in, char *buf, socklen_t size);
    
  • 作用:将 32 位的二进制网络字节序 IPv4 地址转换为点分十进制表示的字符串形式;
  • 参数:
    • in:这是一个 struct in_addr 类型的结构体,该结构体包含了要转换的 32 位二进制网络字节序 IPv4 地址;
    • buf:指向一个字符数组的指针,用于存储转换后的点分十进制字符串;
    • 表示 buf 所指向的字符数组的大小,用于确保不会发生缓冲区溢出;
  • 返回值:
    • 如果转换成功,返回指向 buf 的指针;
    • 如果转换失败(例如 buf 空间不足),则返回 NULL;

2.官方TCP客户端示例代码及分析

在这里插入图片描述

/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#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 "protocol_examples_common.h"
#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sockets.h"


#if defined(CONFIG_EXAMPLE_IPV4)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
#elif defined(CONFIG_EXAMPLE_IPV6)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
#else
#define HOST_IP_ADDR ""
#endif

#define PORT CONFIG_EXAMPLE_PORT

static const char *TAG = "example";
static const char *payload = "Message from ESP32 ";

static void tcp_client_task(void *pvParameters)
{
    char rx_buffer[128];
    char host_ip[] = HOST_IP_ADDR;
    int addr_family = 0;
    int ip_protocol = 0;

    while (1) {
#if defined(CONFIG_EXAMPLE_IPV4)
        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr(host_ip);
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(PORT);
        addr_family = AF_INET;
        ip_protocol = IPPROTO_IP;
#elif defined(CONFIG_EXAMPLE_IPV6)
        struct sockaddr_in6 dest_addr = { 0 };
        inet6_aton(host_ip, &dest_addr.sin6_addr);
        dest_addr.sin6_family = AF_INET6;
        dest_addr.sin6_port = htons(PORT);
        dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
        addr_family = AF_INET6;
        ip_protocol = IPPROTO_IPV6;
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
        struct sockaddr_storage dest_addr = { 0 };
        ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr));
#endif
        int sock =  socket(addr_family, SOCK_STREAM, ip_protocol);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);

        int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
        if (err != 0) {
            ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Successfully connected");

        while (1) {
            int err = send(sock, payload, strlen(payload), 0);
            if (err < 0) {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }

            int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
            // Error occurred during receiving
            if (len < 0) {
                ESP_LOGE(TAG, "recv failed: errno %d", errno);
                break;
            }
            // Data received
            else {
                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                ESP_LOGI(TAG, "%s", rx_buffer);
            }

            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    ESP_ERROR_CHECK(example_connect());

    xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
}

在这里插入图片描述

3.示例代码运行及结果

  • 复制tcp_client例程
    在这里插入图片描述

(1)查看电脑的IP地址

在这里插入图片描述
在这里插入图片描述

(2)下载 TCP 工具并配置

(3)修改后的示例代码

/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#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 "protocol_examples_common.h"
#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/sockets.h"

/**************************TCP************************************ */
#define HOST_IP_ADDR    "192.168.202.156"
#define PORT            6666

static const char *TAG = "TCP";
static const char *payload = "Hello, I am device.";

/**************************WiFi************************************ */
#define WIFI_SSID      "L"
#define WIFI_PASS      "12345678"
#define MAXIMUM_RETRY  5

static EventGroupHandle_t my_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static int s_retry_num = 0;
void my_wifi_connect(void);

static void tcp_client_task(void *pvParameters)
{
    char rx_buffer[128];
    char host_ip[] = HOST_IP_ADDR;
    int addr_family = 0;
    int ip_protocol = 0;
    bool bShutdown = false;
    int err = 0;

    while (1) {
#if defined(CONFIG_EXAMPLE_IPV4)
        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr(host_ip);
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(PORT);
        addr_family = AF_INET;
        ip_protocol = IPPROTO_IP;
#elif defined(CONFIG_EXAMPLE_IPV6)
        struct sockaddr_in6 dest_addr = { 0 };
        inet6_aton(host_ip, &dest_addr.sin6_addr);
        dest_addr.sin6_family = AF_INET6;
        dest_addr.sin6_port = htons(PORT);
        dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
        addr_family = AF_INET6;
        ip_protocol = IPPROTO_IPV6;
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
        struct sockaddr_storage dest_addr = { 0 };
        ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr));
#endif
        int sock =  socket(addr_family, SOCK_STREAM, ip_protocol);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);

        err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
        if (err != 0) {
            ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Successfully connected");

        err = send(sock, payload, strlen(payload), 0);
        if (err < 0) {
            ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
            break;
        }

        while (1) {
            

            int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
            // Error occurred during receiving
            if (len < 0) {
                ESP_LOGE(TAG, "recv failed: errno %d", errno);
                break;
            }
            // Data received
            else {
                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                if(rx_buffer[0] == 's')
                {
                    bShutdown = true;
                    break;
                }
                else
                {
                    err = send(sock, rx_buffer, strlen(rx_buffer), 0);
                    if (err < 0) {
                        ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                        break;
                    }
                }
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                ESP_LOGI(TAG, "%s", rx_buffer);
            }

            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }

        if(bShutdown == true)
            break;

    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    // ESP_ERROR_CHECK(example_connect());
    my_wifi_connect();
    xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
}

static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        if (s_retry_num < MAXIMUM_RETRY) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } else {
            xEventGroupSetBits(my_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(my_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void my_wifi_connect(void)
{
    my_wifi_event_group = xEventGroupCreate();
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
	     .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(my_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 WIFI_SSID, WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 WIFI_SSID, WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    /* The event will not be processed after unregister */
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    vEventGroupDelete(my_wifi_event_group);
}
  • 修改IP地址、端口号、WiFi名称和密码
    在这里插入图片描述

(4)编译、烧录及运行结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、基于LWIP(Light Weight)的TCP服务器

在这里插入图片描述

1.官方TCP服务端示例代码及分析

在这里插入图片描述
在这里插入图片描述

2.修改后的示例代码

  • 将官方示例复制到我们的代码文件夹中;
    在这里插入图片描述
  • 将代码改为下面部分(使用时,需要修改WiFi的名称和密码)
/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#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 "protocol_examples_common.h"

#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>

//? 例程默认为IPV4,即idf.py menuconfig中默认为IPv4,默认定义CONFIG_EXAMPLE_IPV4
#define PORT 6666
#define KEEPALIVE_IDLE 5
#define KEEPALIVE_INTERVAL 5
#define KEEPALIVE_COUNT 5

static const char *TAG = "TCP_Server";

/**************************WiFi Begin************************************ */
#define WIFI_SSID "L"
#define WIFI_PASS "12345678"
#define MAXIMUM_RETRY 5

static EventGroupHandle_t my_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1

static int s_retry_num = 0;
void my_wifi_connect(void);
/**************************WiFi End************************************ */

static void do_retransmit(const int sock)
{
    int len;
    char rx_buffer[128];

    do
    {
        len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0)
        {
            ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
        }
        else if (len == 0)
        {
            ESP_LOGE(TAG, "Connection closed");
        }
        else
        {
            rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
            ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);

            // send() can return less bytes than supplied length.
            // Walk-around for robust implementation.
            int to_write = len;
            while (to_write > 0)
            {
                int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
                if (written < 0)
                {
                    ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                }
                to_write -= written;
            }
        }
    } while (len > 0);
}

static void tcp_server_task(void *pvParameters)
{
    char addr_str[128];
    int addr_family = (int)pvParameters;
    int ip_protocol = 0;
    int keepAlive = 1;
    int keepIdle = KEEPALIVE_IDLE;
    int keepInterval = KEEPALIVE_INTERVAL;
    int keepCount = KEEPALIVE_COUNT;
    struct sockaddr_storage dest_addr;

    if (addr_family == AF_INET)
    {
        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr_ip4->sin_family = AF_INET;
        dest_addr_ip4->sin_port = htons(PORT);
        ip_protocol = IPPROTO_IP;
    }
#ifdef CONFIG_EXAMPLE_IPV6
    else if (addr_family == AF_INET6)
    {
        struct sockaddr_in6 *dest_addr_ip6 = (struct sockaddr_in6 *)&dest_addr;
        bzero(&dest_addr_ip6->sin6_addr.un, sizeof(dest_addr_ip6->sin6_addr.un));
        dest_addr_ip6->sin6_family = AF_INET6;
        dest_addr_ip6->sin6_port = htons(PORT);
        ip_protocol = IPPROTO_IPV6;
    }
#endif

    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0)
    {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
    // Note that by default IPV6 binds to both protocols, it is must be disabled
    // if both protocols used at the same time (used in CI)
    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endif

    ESP_LOGI(TAG, "Socket created");

    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0)
    {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    err = listen(listen_sock, 1);
    if (err != 0)
    {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1)
    {

        ESP_LOGI(TAG, "Socket listening");

        struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
        socklen_t addr_len = sizeof(source_addr);
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0)
        {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }

        // Set tcp keepalive option
        setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
        setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
        // Convert ip address to string
        if (source_addr.ss_family == PF_INET)
        {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
        }
#ifdef CONFIG_EXAMPLE_IPV6
        else if (source_addr.ss_family == PF_INET6)
        {
            inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
        }
#endif
        ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);

        do_retransmit(sock);

        shutdown(sock, 0);
        close(sock);
    }

CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    // ESP_ERROR_CHECK(example_connect());
    my_wifi_connect();

#ifdef CONFIG_EXAMPLE_IPV4
    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void *)AF_INET, 5, NULL);
#endif
#ifdef CONFIG_EXAMPLE_IPV6
    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void *)AF_INET6, 5, NULL);
#endif
}

static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        if (s_retry_num < MAXIMUM_RETRY)
        {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        }
        else
        {
            xEventGroupSetBits(my_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG, "connect to the AP fail");
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(my_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void my_wifi_connect(void)
{
    my_wifi_event_group = xEventGroupCreate();
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
	     .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(my_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 WIFI_SSID, WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 WIFI_SSID, WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    /* The event will not be processed after unregister */
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    vEventGroupDelete(my_wifi_event_group);
}

3.运行结果

  • 设置芯片型号、编译、烧录
idf.py set-target esp32s3		#设置芯片型号
idf.py flash monitor			#编译、烧录程序并查看调试信息

在这里插入图片描述

六、UDP客户端

1.UDP与TCP的差异

在这里插入图片描述
在这里插入图片描述

2.UPD客户端示例代码

  • 示例代码位置
    在这里插入图片描述

3.修改后的示例代码

  • 将示例代码复制到我们的代码路径下
    在这里插入图片描述
  • 下载UDP工具地址
    在这里插入图片描述
    在这里插入图片描述
  • 命令提示行输入"ipconfig"查看IP地址
    在这里插入图片描述
  • 修改后的示例代码(还需要根据自己电脑的IP地址以及WiFi的名称和密码修改)
    在这里插入图片描述
/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#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 "protocol_examples_common.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include "addr_from_stdin.h"

#define HOST_IP_ADDR "192.168.202.156"
#define PORT 6666

static const char *TAG = "Socket_UDP";
static const char *payload = "I am UDP Client.";
/**************************WiFi Begin************************************ */
#define WIFI_SSID "L"
#define WIFI_PASS "12345678"
#define MAXIMUM_RETRY 5

static EventGroupHandle_t my_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1

static int s_retry_num = 0;
void my_wifi_connect(void);
/**************************WiFi End************************************ */

static void udp_client_task(void *pvParameters)
{
    char rx_buffer[128];
    char host_ip[] = HOST_IP_ADDR;
    int addr_family = 0;
    int ip_protocol = 0;

    while (1)
    {

#if defined(CONFIG_EXAMPLE_IPV4)
        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(PORT);
        addr_family = AF_INET;
        ip_protocol = IPPROTO_IP;
#elif defined(CONFIG_EXAMPLE_IPV6)
        struct sockaddr_in6 dest_addr = {0};
        inet6_aton(HOST_IP_ADDR, &dest_addr.sin6_addr);
        dest_addr.sin6_family = AF_INET6;
        dest_addr.sin6_port = htons(PORT);
        dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
        addr_family = AF_INET6;
        ip_protocol = IPPROTO_IPV6;
#elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
        struct sockaddr_storage dest_addr = {0};
        ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_DGRAM, &ip_protocol, &addr_family, &dest_addr));
#endif

        int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
        if (sock < 0)
        {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket created, sending to %s:%d", HOST_IP_ADDR, PORT);

        int err = sendto(sock, payload, strlen(payload), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if (err < 0)
        {
            ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Message sent");

        while (1)
        {
            struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
            socklen_t socklen = sizeof(source_addr);
            int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);

            // Error occurred during receiving
            if (len < 0)
            {
                ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
                break;
            }
            // Data received
            else
            {
                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                ESP_LOGI(TAG, "%s", rx_buffer);
                if (strncmp(rx_buffer, "reset: ", 5) == 0)
                {
                    ESP_LOGI(TAG, "Received expected message, re-init");
                    break;
                }
                else
                {
                    err = sendto(sock, rx_buffer, len, 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
                    if (err < 0)
                    {
                        ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                        break;
                    }
                    ESP_LOGI(TAG, "Message sent");
                }
            }

            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }

        if (sock != -1)
        {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    // ESP_ERROR_CHECK(example_connect());
    my_wifi_connect();

    xTaskCreate(udp_client_task, "udp_client", 4096, NULL, 5, NULL);
}

static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        if (s_retry_num < MAXIMUM_RETRY)
        {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        }
        else
        {
            xEventGroupSetBits(my_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG, "connect to the AP fail");
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(my_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void my_wifi_connect(void)
{
    my_wifi_event_group = xEventGroupCreate();
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(my_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT)
    {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 WIFI_SSID, WIFI_PASS);
    }
    else if (bits & WIFI_FAIL_BIT)
    {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 WIFI_SSID, WIFI_PASS);
    }
    else
    {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    /* The event will not be processed after unregister */
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    vEventGroupDelete(my_wifi_event_group);
}

4.编译、烧录及运行结果

  • 设置芯片型号、编译烧录查看调试信息
# 设置芯片型号
idf.py set-target esp32s3

#编译烧录查看调试信息
idf.py flash monitor

在这里插入图片描述

七、UDP服务器

1.UDP服务器示例代码

在这里插入图片描述

  • 将示例代码复制到我们的代码路径下
    在这里插入图片描述
    在这里插入图片描述
  • socket.h路径
    在这里插入图片描述

2.修改后的示例代码

  • 需要修改自己的WiFi名称和密码
    在这里插入图片描述
/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#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 "protocol_examples_common.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include "addr_from_stdin.h"

#define PORT 6666

static const char *TAG = "UDP_Server";

/**************************WiFi Begin************************************ */
#define WIFI_SSID "L"
#define WIFI_PASS "12345678"
#define MAXIMUM_RETRY 5

static EventGroupHandle_t my_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1

static int s_retry_num = 0;
void my_wifi_connect(void);
/**************************WiFi End************************************ */

static void udp_server_task(void *pvParameters)
{
    char rx_buffer[128];
    char addr_str[128];
    int addr_family = (int)pvParameters;
    int ip_protocol = 0;
    struct sockaddr_in6 dest_addr;

    while (1)
    {

        if (addr_family == AF_INET)
        {
            struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
            dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
            dest_addr_ip4->sin_family = AF_INET;
            dest_addr_ip4->sin_port = htons(PORT);
            ip_protocol = IPPROTO_IP;
        }
        else if (addr_family == AF_INET6)
        {
            bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
            dest_addr.sin6_family = AF_INET6;
            dest_addr.sin6_port = htons(PORT);
            ip_protocol = IPPROTO_IPV6;
        }

        int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
        if (sock < 0)
        {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket created");

#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
        if (addr_family == AF_INET6)
        {
            // Note that by default IPV6 binds to both protocols, it is must be disabled
            // if both protocols used at the same time (used in CI)
            int opt = 1;
            setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
            setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
        }
#endif

        int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
        if (err < 0)
        {
            ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        }
        ESP_LOGI(TAG, "Socket bound, port %d", PORT);

        u16_t iPort = 0;
        while (1)
        {

            ESP_LOGI(TAG, "Waiting for data");
            struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
            socklen_t socklen = sizeof(source_addr);
            int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);

            // Error occurred during receiving
            if (len < 0)
            {
                ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
                break;
            }
            // Data received
            else
            {
                // Get the sender's ip address as string
                if (source_addr.ss_family == PF_INET)
                {
                    inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
                    iPort = ntohs(((struct sockaddr_in *)&source_addr)->sin_port);
                }
                else if (source_addr.ss_family == PF_INET6)
                {
                    inet6_ntoa_r(((struct sockaddr_in6 *)&source_addr)->sin6_addr, addr_str, sizeof(addr_str) - 1);
                    iPort = ntohs(((struct sockaddr_in6 *)&source_addr)->sin6_port);
                }

                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string...
                ESP_LOGI(TAG, "Received %d bytes from %s: Port:%d", len, addr_str, iPort);
                ESP_LOGI(TAG, "%s", rx_buffer);

                int err = sendto(sock, rx_buffer, len, 0, (struct sockaddr *)&source_addr, sizeof(source_addr));
                if (err < 0)
                {
                    ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                    break;
                }
            }
        }

        if (sock != -1)
        {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    // ESP_ERROR_CHECK(example_connect());
    my_wifi_connect();

#ifdef CONFIG_EXAMPLE_IPV4
    xTaskCreate(udp_server_task, "udp_server", 4096, (void *)AF_INET, 5, NULL);
#endif
#ifdef CONFIG_EXAMPLE_IPV6
    xTaskCreate(udp_server_task, "udp_server", 4096, (void *)AF_INET6, 5, NULL);
#endif
}

static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
    {
        esp_wifi_connect();
    }
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
        if (s_retry_num < MAXIMUM_RETRY)
        {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        }
        else
        {
            xEventGroupSetBits(my_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG, "connect to the AP fail");
    }
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
    {
        ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(my_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void my_wifi_connect(void)
{
    my_wifi_event_group = xEventGroupCreate();
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
            .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(my_wifi_event_group,
                                           WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                           pdFALSE,
                                           pdFALSE,
                                           portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT)
    {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 WIFI_SSID, WIFI_PASS);
    }
    else if (bits & WIFI_FAIL_BIT)
    {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 WIFI_SSID, WIFI_PASS);
    }
    else
    {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    /* The event will not be processed after unregister */
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    vEventGroupDelete(my_wifi_event_group);
}

3.编译、烧录及运行结果

  • 设置芯片型号、编译烧录查看调试信息
# 设置芯片型号
idf.py set-target esp32s3

#编译烧录查看调试信息
idf.py flash monitor

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到