ESP32 IDF开发 应用篇⑭ Wifi TCP客户端和服务器通讯

张裕  金牌会员 | 2024-8-24 17:30:49 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 446|帖子 446|积分 1338

别迷路-导航栏
快速导航找到你想要的(文章目录)

此篇文章如果对你有用,请点赞收藏,您的支持就是博主坚持的动力。
1、博主写这篇技术文章的目标:

(1)、了解 tcp udp的通讯特点;
(2)、了解客户端和服务器的特点;
(3)、掌握tcp客户端和服务器编程;
2、 概述

在前面章节已经为大家先容了ESP32 WIFI STA 及AP模式的基本应用,本章是基于wifi sta模式开发。Tcp的理论知识这里就不过多先容,大家百度一下比我解提及来好多了。
(1) Tcp 与udp的区别及各自的特点
TCP:
 •面向连接的:发数据前要举行连接。
 •可靠的连接:TCP 连接传送的数据,无不对,不丢失,不重复,且按序到达。
 •点到点:TCP 连接传送的数据,无不对,不丢失,不重复,且按序到达
 •最大长度有限:仅 1500 字节。(http 和 websocket 都是采用TCP通讯)
UDP:
 •无连接的:发数据前不需要创建连接。
 •不可靠:尽最大努力交付,即不保证可靠交付。
 •支持一对一,一对多,多对一和多对多的交互通讯
 •占用资源少,发送数据快。
(2) 客户端与服务端的区别
服务端:创建并监听多个连接,服务端可以监听多个客户端发过来的连接;
客户端:创建并发起连接,多个客户端可以连接到同一个服务端;
3、 Socket API库的先容
1、 TCP 编程的客户端一般步调是:
(1)创建一个 socket,用函数 socket();
(2)设置 socket 属性,用函数 setsockopt();(可选)
(3)绑定 IP 地点、端口等信息到 socket 上,用函数 bind();* 可选
(4)设置要连接的对方的 IP 地点和端口等属性;
(5)连接服务器,用函数 connect();
(6) 收发数据,用函数 send()和 recv(),大概 read()和 write();
(7)关闭网络连接;
2、TCP 编程的服务器端一般步调是:
(1)创建一个 socket,用函数 socket();
(2)设置 socket 属性,用函数 setsockopt();(可选)
(3)绑定 IP 地点、端口等信息到 socket 上,用函数 bind();
(4)开启监听,用函数 listen();
(5)接收客户端上来的连接,用函数 accept();
(6)收发数据,用函数 send()和 recv(),大概 read()和 write();
(7)关闭网络连接; closesocket();
(8)关闭监听;
ESP32 使用的是 LwIP,LwIP 是特殊适用于嵌入式装备的小型开源 TCP/IP 协议栈,对内
存资源占用很小。ESP-IDF 即是移植了 LwIP 协议栈。
我们这里直接使用标准 socket API接口,API函数只做简单的先容,跟详细的socket API大家可以百度
 创建连接 socket();
 连接服务端函数:connect();
 关闭 socket 函数:close();
 获取 socket 错误代码:getsocketopt();
 接收数据函数:recv();
 发送数据函数:send();
 绑定函数:bing();
 监听函数:listen();
 获取连接函数:accept();
更多API 参考esp-idf\components\lwip\lwip\src\include\lwip\sockets.h
4、 软件设计

(1)、客户端软件设计
①、等候wifi sta连接好路由器
②、创建连接服务端的socket
client_connect_socket = socket(AF_INET, SOCK_STREAM, 0);
③、设置连接服务器信息,端口和IP
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(ip);
④、然后开始连接
connect(client_connect_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)
⑤收发数据使用recv();send();函数
(2)、服务端软件设计
①、等候wifi sta连接好路由器
②、创建服务端的socket
③、设置新建的server socket参数
④、绑定ip和端口号bind()
⑤、开始监听有没有客户端连接上来
listen(server_socket, 1) 而且设置允许最大连接客户端的数量
⑥、获取连接的socket
accept(server_socket, (struct sockaddr *)&client_addr, &socklen)
⑦、收发数据使用recv();send();函数
5、 实例

复制wifi sta工程改名字为 idf_wifi_tcp(由于本例程是基于wifi sta)即可 文件名字改为 idf_wifi_tcp.C makefile文件也改成 PROJECT_NAME := idf_wifi_tcp即可,然后复制一下代码测试。
在编译之前,为大家收支一个知识点,解说一下如安在工程下面创建一个文件夹来管理.c和.h文件。
以此工程为例解说
(1)、在idf_wifi_tcp文件夹下面新建文件夹network,而且添加自己的.c和.h文件
(2)、在network文件夹下面添加文件component.mk,文件内容为COMPONENT_ADD_INCLUDEDIRS := .


(3)、然后在工程的Makefile文件下面添加编译路径:
EXTRA_COMPONENT_DIRS := $(IDF_PATH)/examples/lyy/idf_wifi_tcp/network

(4)、最后make clean
make flash monitor
代码:idf_wifi_tcp.c文件
  1. /**********************************************************************
  2. *               文件名:            idf_wifi_tcp.c
  3. *               创建人:            
  4. *               创建日期:           
  5. *               修改人:
  6. *               修改日期:           
  7. *               版本号:            V1.1
  8. *               备注:
  9. *               公司:
  10. ********************************************************************/
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <unistd.h>
  14. #include "freertos/FreeRTOS.h"
  15. #include "freertos/task.h"
  16. #include "freertos/event_groups.h"
  17. #include "nvs.h"
  18. #include "esp_log.h"
  19. #include "nvs_flash.h"
  20. #include "esp_system.h"
  21. #include "esp_wifi.h"
  22. #include "esp_event.h"
  23. #include "lwip/err.h"
  24. #include "lwip/sys.h"
  25. #include "tcp_bsp.h"
  26. static const char *TAG = "WIFI_TCP";
  27. //事件标志组
  28. EventGroupHandle_t xCreatedEventGroup_WifiConnect = NULL;
  29. static int retry_num = 0;
  30. /***********************************************************************
  31. * 函数:  
  32. * 描述:   wifi 回调函数
  33. * 参数:
  34. * 返回: 无
  35. * 备注:
  36. ************************************************************************/
  37. static void event_handler(void* arg, esp_event_base_t event_base,int32_t event_id, void* event_data)
  38. {
  39.     ESP_LOGI(TAG, "event_base: %s  ; event_id : %d",event_base,event_id);
  40.     if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
  41.     {
  42.         esp_wifi_connect();//开始连接
  43.     }
  44.     else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) //wifi断开时
  45.     {
  46.         if (retry_num < CONFIG_ESP_MAXIMUM_RETRY) //重新连接次数
  47.         {
  48.             esp_wifi_connect();
  49.             retry_num++;
  50.             ESP_LOGI(TAG, "retry to connect to the AP");
  51.         }
  52.         else
  53.         {
  54.             xEventGroupSetBits(xCreatedEventGroup_WifiConnect, WIFI_FAIL_BIT);
  55.             xEventGroupClearBits(xCreatedEventGroup_WifiConnect, WIFI_CONNECTED_BIT);
  56.         }
  57.         ESP_LOGI(TAG,"connect to the AP fail");
  58.     }
  59.     else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)//获得IP
  60.     {
  61.         ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
  62.         ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
  63.         retry_num = 0;
  64.         xEventGroupSetBits(xCreatedEventGroup_WifiConnect, WIFI_CONNECTED_BIT);
  65.         xEventGroupClearBits(xCreatedEventGroup_WifiConnect, WIFI_FAIL_BIT);
  66.     }
  67. }
  68. /***********************************************************************
  69. * 函数:  
  70. * 描述:   wifi sta初始化代码相对比较固定,一般不需要做修改
  71. * 参数:
  72. * 返回: 无
  73. * 备注:
  74. ************************************************************************/
  75. void wifi_init_sta(void)
  76. {
  77.     ESP_ERROR_CHECK(esp_netif_init());//初始化TCP / IP堆栈和esp-netif
  78.     ESP_ERROR_CHECK(esp_event_loop_create_default()); //创建默认event loop
  79.     esp_netif_create_default_wifi_sta();//创建默认的WIFI STA。
  80.     wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();//给wifi_init_config_t结构体初始化系统默认的数据
  81.     ESP_ERROR_CHECK(esp_wifi_init(&cfg));//使用默认的参数初始化wifi
  82.     //注册wifi 连接过程中回调函数
  83.     ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
  84.     ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
  85.     //设置账户和密码,在menuconfig中设置
  86.     wifi_config_t wifi_config = {
  87.         .sta = {
  88.             .ssid = CONFIG_ESP_WIFI_SSID,
  89.             .password = CONFIG_ESP_WIFI_PASSWORD
  90.             //.ssid = "ssid",
  91.             //.password = "password"
  92.         },
  93.     };
  94.     ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );//设置为sta模式
  95.     ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
  96.     ESP_ERROR_CHECK(esp_wifi_start() );//启动
  97.     ESP_LOGI(TAG, "wifi_init_sta finished.");
  98. }
  99. /***********************************************************************
  100. * 函数:  
  101. * 描述:   主函数
  102. * 参数:
  103. * 返回: 无
  104. * 备注:
  105. ************************************************************************/
  106. void app_main()
  107. {   
  108.     //初始化NVS
  109.    esp_err_t ret = nvs_flash_init();
  110.     if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
  111.       ESP_ERROR_CHECK(nvs_flash_erase());
  112.       ret = nvs_flash_init();
  113.     }
  114.     ESP_ERROR_CHECK(ret);
  115.     ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
  116.     //创建事件标志组,用于等待wifi连接
  117.     xCreatedEventGroup_WifiConnect = xEventGroupCreate();
  118.     //WIFI sta初始化
  119.     wifi_init_sta();
  120.     AppTaskCreate();
  121. }
复制代码
代码:tcp_bsp.c文件
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <errno.h>
  4. #include <stdlib.h>
  5. #include "sdkconfig.h"
  6. #include "freertos/FreeRTOS.h"
  7. #include "freertos/task.h"
  8. #include "esp_system.h"
  9. #include "esp_spi_flash.h"
  10. #include "esp_wifi.h"
  11. #include <sys/socket.h>
  12. #include "freertos/event_groups.h"
  13. #include "esp_event.h"
  14. #include "esp_err.h"
  15. #include "nvs_flash.h"
  16. #include "esp_smartconfig.h"
  17. #include "esp_netif.h"
  18. #include "esp_wpa2.h"
  19. #include "esp_log.h"
  20. #include "esp_ota_ops.h"
  21. #include "nvs.h"
  22. #include "tcp_bsp.h"
  23. //任务句柄
  24. static TaskHandle_t xHandleTaskTcpClinent = NULL;
  25. static TaskHandle_t xHandleTaskTcpServer = NULL;
  26. //任务堆栈大小,主要是函数嵌套参数入栈和局部变量的内存
  27. #define TcpClinent_TASK_STACK_SIZE      8192
  28. #define TcpServer_TASK_STACK_SIZE       8192
  29. //任务优先级,越大越高,跟ucos相反
  30. #define TcpClinent_TASK_PRIO            2
  31. #define TcpServer_TASK_PRIO             3
  32. static int client_connect_socket = 0;                     //客户端连接socket
  33. static int server_socket = 0;                           //服务器创建的socket
  34. static int server_connect_socket = 0;                   //有客户端连接到服务器的socket
  35. //函数声明
  36. static esp_err_t CreateTcpClient(const char *ip,uint16_t port);
  37. static esp_err_t CreateTcpServer(bool isCreatServer,uint16_t port);
  38. static void vTaskTcpClinent(void *pvParameters);
  39. static void vTaskTcpServer(void *pvParameters);
  40. /***********************************************************************
  41. * 函数:  
  42. * 描述:   创建tcp接收发送任务,给外部使用
  43. * 参数:
  44. * 返回: 无
  45. * 备注:
  46. ************************************************************************/
  47. void AppTaskCreate(void)
  48. {
  49.     xTaskCreate(vTaskTcpClinent,                    //任务函数
  50.             "vTaskTcpClinent",                      // 任务名
  51.             TcpClinent_TASK_STACK_SIZE,             // 任务栈大小,单位 word,也就是 4 字节
  52.             NULL,                                   //任务参数
  53.             TcpClinent_TASK_PRIO,                   //任务优先级
  54.             &xHandleTaskTcpClinent);   
  55.     xTaskCreate(vTaskTcpServer,                     //任务函数
  56.             "vTaskTcpServer",                       // 任务名
  57.             TcpServer_TASK_STACK_SIZE,              // 任务栈大小,单位 word,也就是 4 字节
  58.             NULL,                                   //任务参数
  59.             TcpServer_TASK_PRIO,                    //任务优先级
  60.             &xHandleTaskTcpServer);
  61. }
  62. /***********************************************************************
  63. * 函数:  
  64. * 描述:   创建tcp客户端连接
  65. * 参数:ip:IP地址,port:端口号
  66. * 返回: 无
  67. * 备注:
  68. ************************************************************************/
  69. static esp_err_t CreateTcpClient(const char *ip,uint16_t port)
  70. {
  71.     static struct sockaddr_in server_addr;                  //server地址
  72.     //等待连接成功,或已经连接有断开连接,此函数会一直阻塞,只有有连接
  73.    EventBits_t bits =  xEventGroupWaitBits(xCreatedEventGroup_WifiConnect,// 事件标志组句柄
  74.                 WIFI_CONNECTED_BIT, // 等待bit0和bit1被设置
  75.                 pdFALSE,                            //TRUE退出时bit0和bit1被清除,pdFALSE退出时bit0和bit1不清除
  76.                 pdFALSE,                            //设置为pdTRUE表示等待bit1和bit0都被设置,pdFALSE表示等待bit1或bit0其中一个被设置
  77.                 portMAX_DELAY);                     //等待延迟时间,一直等待
  78.     if (bits & WIFI_CONNECTED_BIT)
  79.     {
  80.         //新建socket
  81.         client_connect_socket = socket(AF_INET, SOCK_STREAM, 0);
  82.         if (client_connect_socket < 0)
  83.         {
  84.             close(client_connect_socket);
  85.             return ESP_FAIL;
  86.         }
  87.         //配置连接服务器信息
  88.         server_addr.sin_family = AF_INET;
  89.         server_addr.sin_port = htons(port);
  90.         server_addr.sin_addr.s_addr = inet_addr(ip);
  91.         //连接服务器
  92.         if (connect(client_connect_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
  93.         {
  94.             close(client_connect_socket);
  95.             return ESP_FAIL;
  96.         }
  97.         return ESP_OK;
  98.     }
  99.     else
  100.     {
  101.         return ESP_FAIL;
  102.     }
  103. }
  104. /***********************************************************************
  105. * 函数:  
  106. * 描述:   客户端任务
  107. * 参数:
  108. * 返回: 无
  109. * 备注:
  110. ************************************************************************/
  111. static void vTaskTcpClinent(void *pvParameters)
  112. {
  113.     int len = 0;            //长度
  114.     uint8_t databuff[512];    //缓存
  115.     //创建tcp客户端
  116.     if (CreateTcpClient(TCP_SERVER_ADRESS,TCP_SERVER_PORT) == ESP_OK)
  117.     {
  118.         ESP_LOGI("vTaskTcpClinent", "connect OK");
  119.     }
  120.     else
  121.     {
  122.         ESP_LOGI("vTaskTcpClinent", "connect FAIL");
  123.     }
  124.     for (;;)
  125.     {
  126.         //读取接收数据
  127.         len = recv(client_connect_socket, databuff, sizeof(databuff), 0);//阻塞函数
  128.         if (len > 0)//接收到数据
  129.         {
  130.             send(client_connect_socket, databuff, len, 0);
  131.         }
  132.         else
  133.         {
  134.             //断开连接
  135.             if (CreateTcpClient(TCP_SERVER_ADRESS,TCP_SERVER_PORT)== ESP_OK)
  136.             {
  137.                 ESP_LOGI("vTaskTcpClinent", "connect OK");
  138.             }
  139.             vTaskDelay(500 / portTICK_PERIOD_MS);
  140.         }
  141.         vTaskDelay(50 / portTICK_PERIOD_MS);
  142.     }
  143. }
  144. /***********************************************************************
  145. * 函数:  
  146. * 描述:   创建服务端监听任务
  147. * 参数:isCreatServer:true创建socket并监听,false只监听,port监听的端口号
  148. * 返回: 无
  149. * 备注:
  150. ************************************************************************/
  151. static esp_err_t CreateTcpServer(bool isCreatServer,uint16_t port)
  152. {
  153.     static struct sockaddr_in client_addr;   
  154.     static unsigned int socklen = sizeof(client_addr);      //地址长度
  155.     //等待连接成功,或已经连接有断开连接,此函数会一直阻塞,只有有连接
  156.    EventBits_t bits =  xEventGroupWaitBits(xCreatedEventGroup_WifiConnect,// 事件标志组句柄
  157.                 WIFI_CONNECTED_BIT,              // 等待bit0和bit1被设置
  158.                 pdFALSE,                            //TRUE退出时bit0和bit1被清除,pdFALSE退出时bit0和bit1不清除
  159.                 pdFALSE,                            //设置为pdTRUE表示等待bit1和bit0都被设置,pdFALSE表示等待bit1或bit0其中一个被设置
  160.                 portMAX_DELAY);                     //等待延迟时间,一直等待
  161.     if (bits & WIFI_CONNECTED_BIT)
  162.     {
  163.         if (isCreatServer == true)
  164.         {
  165.             server_socket = socket(AF_INET, SOCK_STREAM, 0);
  166.             if (server_socket < 0)
  167.             {
  168.                 close(server_socket);
  169.                 return ESP_FAIL;
  170.             }
  171.             //配置新建server socket参数
  172.             client_addr.sin_family = AF_INET;
  173.             client_addr.sin_port = htons(port);
  174.             client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  175.             //bind:地址的绑定
  176.             if (bind(server_socket, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0)
  177.             {
  178.                 //bind失败后,关闭新建的socket,等待下次新建
  179.                 close(server_socket);
  180.                 return ESP_FAIL;
  181.             }
  182.         }
  183.         
  184.         //listen,下次时,直接监听
  185.         if (listen(server_socket, 1) < 0)
  186.         {
  187.             //listen失败后,关闭新建的socket,等待下次新建
  188.             close(server_socket);
  189.             return ESP_FAIL;
  190.         }
  191.         //accept,搜寻全连接队列
  192.         server_connect_socket = accept(server_socket, (struct sockaddr *)&client_addr, &socklen);
  193.         if (server_connect_socket < 0)
  194.         {
  195.             //accept失败后,关闭新建的socket,等待下次新建
  196.             close(server_socket);
  197.             return ESP_FAIL;
  198.         }
  199.         return ESP_OK;
  200.     }
  201.     else
  202.     {
  203.         return ESP_FAIL;
  204.     }
  205. }
  206. /***********************************************************************
  207. * 函数:  
  208. * 描述:   服务端任务
  209. * 参数:
  210. * 返回: 无
  211. * 备注:
  212. ************************************************************************/
  213. static void vTaskTcpServer(void *pvParameters)
  214. {
  215.     int len = 0;            //长度
  216.     uint8_t databuff[512];    //缓存
  217.     //创建tcp客户端
  218.     CreateTcpServer(true,TCP_CLIENT_PORT);
  219.     for (;;)
  220.     {
  221.         //读取接收数据
  222.         len = recv(server_connect_socket, databuff, sizeof(databuff), 0);//阻塞函数
  223.         if (len > 0)//接收到数据
  224.         {
  225.             send(server_connect_socket, databuff, len, 0);
  226.         }
  227.         else
  228.         {
  229.             //断开连接
  230.             CreateTcpServer(false,TCP_CLIENT_PORT);  
  231.             vTaskDelay(500 / portTICK_PERIOD_MS);
  232.         }
  233.         vTaskDelay(50 / portTICK_PERIOD_MS);
  234.     }
  235. }
复制代码
代码:tcp_bsp.h文件
  1. #ifndef __TCP_BSP_H__
  2. #define __TCP_BSP_H__
  3. #ifdef __cplusplus
  4. extern "C" {
  5. #endif
  6. #define TCP_SERVER_ADRESS       "192.168.33.16"     //作为client,要连接TCP服务器地址
  7. #define TCP_SERVER_PORT          5200               //服务端端口
  8. #define TCP_CLIENT_PORT          5000               //TCP客户端端口
  9. #define WIFI_CONNECTED_BIT  BIT0
  10. #define WIFI_FAIL_BIT       BIT1
  11. extern EventGroupHandle_t xCreatedEventGroup_WifiConnect;
  12. void AppTaskCreate(void);
  13. #ifdef __cplusplus
  14. }
  15. #endif
  16. #endif /*#ifndef __TCP_BSP_H__*/
复制代码
6、 调试结果

方法:打开网络调试助手这是这是为TCP Server,根据IP调解代码中的IP地点和端口
  1. #define TCP_SERVER_ADRESS       "192.168.33.16"     //作为client,要连接TCP服务器地址
  2. #define TCP_SERVER_PORT          5200               //服务端端口
  3. #define TCP_CLIENT_PORT          5000               //TCP客户端端口
复制代码



所有文章源代码:https://download.csdn.net/download/lu330274924/88518092

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

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

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

本版积分规则

张裕

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

标签云

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