当前位置: 首页 > news >正文

ESP32-C3_TCP

这章主要讲述下 TCP
1:环境
VSCODE+IDF5.4
ESP32-C3

2:直接上代码

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_http_client.h"
#include "esp_netif.h"  // 替代 tcpip_adapter 的新网络接口组件
#include <string.h>// 配置
#define WIFI_SSID "*******"
#define WIFI_PASSWORD "********"
#define SERVER_HOST "192.168.1.3"
#define SERVER_PORT 8888
#define QUEUE_LEN 10        // 队列长度
#define DATA_BUF_SIZE 1024  // 数据缓冲区大小// 日志标签
static const char *TAG = "WIFI_TCP_EXAMPLE";// 数据结构体(队列中传递的数据格式)
typedef struct {char data[DATA_BUF_SIZE];int len;  // 实际数据长度
} DataPacket;// 全局队列句柄(发送和接收队列)
QueueHandle_t tx_queue;  // 处理→发送
QueueHandle_t rx_queue;  // 接收→处理// TCP连接句柄(全局,供发送/接收任务使用,需确保线程安全)
int g_socket_fd = -1;
bool b_connect =false ;static bool ready_connect();
// 事件组:用于等待 WiFi 连接成功
static EventGroupHandle_t s_wifi_event_group;
const int WIFI_CONNECTED_BIT = BIT0;
// 1. 发送任务:从tx_queue取数据,通过TCP发送
static void tx_task(void *pvParameters) {DataPacket packet;while (1) {// 等待队列中有数据(阻塞等待,超时100ms)if (xQueueReceive(tx_queue, &packet, pdMS_TO_TICKS(100)) == pdTRUE) {if (g_socket_fd == -1) {ESP_LOGE(TAG,"SEND TASK:disconnect ,data throw");// printf("发送任务:未连接,丢弃数据\n");continue;}// 发送数据到服务器ssize_t sent = send(g_socket_fd, packet.data, packet.len, 0);if (sent < 0) {//printf("发送失败\n");ESP_LOGE(TAG,"SEND fail,retry?");// 可在此处触发重连逻辑} else {ESP_LOGE(TAG,"SEND SUCCESS %.*s",packet.len, packet.data);// printf("发送成功:%.*s\n", packet.len, packet.data);}}}
}// 2. 接收任务:从TCP读取数据,存入rx_queue
static void rx_task(void *pvParameters) {DataPacket packet;while (1) {if (g_socket_fd == -1) {vTaskDelay(pdMS_TO_TICKS(100));  // 未连接则休眠if(b_connect){ready_connect();}continue;}// 从TCP读取数据(非阻塞,避免卡死)ssize_t recv_len = recv(g_socket_fd, packet.data, DATA_BUF_SIZE-1, 0);if (recv_len > 0) {packet.len = recv_len;packet.data[recv_len] = '\0';  // 确保字符串结束ESP_LOGE(TAG,"RECV_DATA:%.*s", recv_len, packet.data);// 存入接收队列(若队列满则丢弃)if (xQueueSend(rx_queue, &packet, 0) != pdTRUE) {ESP_LOGE(TAG,"RECV QUEUE IS FULL");}} else if (recv_len < 0) {ESP_LOGE(TAG,"SERVER DISCONNECT[%d]",recv_len);//  closesocket(g_socket_fd);g_socket_fd = -1;  // 标记连接断开//  b_connect =false;break;  // 退出任务,等待重连后重启}vTaskDelay(pdMS_TO_TICKS(10));  // 短暂休眠,降低CPU占用}
}// 3. 处理任务:从rx_queue取数据,处理后将结果存入tx_queue
static void process_task(void *pvParameters) {DataPacket in_packet, out_packet;while (1) {// 等待接收队列有数据(阻塞等待)if (xQueueReceive(rx_queue, &in_packet, portMAX_DELAY) == pdTRUE) {// 示例处理逻辑:将收到的数据加上"Reply: "前缀,作为响应snprintf(out_packet.data, DATA_BUF_SIZE, "Reply: %.*s", in_packet.len, in_packet.data);out_packet.len = strlen(out_packet.data);// 发送到发送队列if (xQueueSend(tx_queue, &out_packet, pdMS_TO_TICKS(100)) != pdTRUE) {//   printf("发送队列满,处理结果丢弃\n");ESP_LOGE(TAG,"SEND QUEUE IS  FULL,MSG throw");}}}
}static bool ready_connect(){char host_ip[] = SERVER_HOST;int addr_family = 0;int ip_protocol = 0;#define PORT SERVER_PORT
#if defined(CONFIG_EXAMPLE_IPV4)struct sockaddr_in dest_addr;inet_pton(AF_INET, host_ip, &dest_addr.sin_addr);dest_addr.sin_family = AF_INET;dest_addr.sin_port = htons(SERVER_PORT);addr_family = AF_INET;ip_protocol = IPPROTO_IP;
#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));
#endifint sock =  socket(addr_family, SOCK_STREAM, ip_protocol);if (sock < 0) {ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);return false ;}ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));if (err != 0) {ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);return false ;}ESP_LOGI(TAG, "Successfully connected[%d]",sock);g_socket_fd = sock;//赋值b_connect= true;return true ;
}
// TCP连接函数(连接成功后启动收发任务)
static void tcp_connect(void) {// (省略创建socket、connect的过程,参考之前的示例)if(!ready_connect()){ESP_LOGI(TAG, "fail connected");return ;}// 假设连接成功,g_socket_fd被赋值为有效句柄//  printf("TCP连接成功\n");// 启动/重启收发任务(确保任务只运行一个实例)static TaskHandle_t rx_task_handle = NULL;static TaskHandle_t tx_task_handle = NULL;if (rx_task_handle == NULL) {xTaskCreate(rx_task, "rx_task", 4096, NULL, 5, &rx_task_handle);} else {vTaskResume(rx_task_handle);  // 若已存在则唤醒}if (tx_task_handle == NULL) {xTaskCreate(tx_task, "tx_task", 4096, NULL, 5, &tx_task_handle);}ESP_LOGI(TAG,"create task finish[%d]",g_socket_fd);{//临时发送一条数据DataPacket packet;packet.len= strlen("hello server");//   memset(packet.data,0,sizeof(packet.data));strncpy(packet.data,"hello server", packet.len+1);packet.data[packet.len]=0;ESP_LOGI(TAG,"send to server data=%.*s",packet.len,packet.data);ssize_t sent = send(g_socket_fd, packet.data, packet.len, 0);if (sent < 0) {//printf("发送失败\n");ESP_LOGE(TAG,"SEND fail,retry?");// 可在此处触发重连逻辑} else {ESP_LOGE(TAG,"SEND SUCCESS %.*s",packet.len, packet.data);// printf("发送成功:%.*s\n", packet.len, packet.data);}}
}// WiFi 事件处理函数
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) {// WiFi 启动后开始连接esp_wifi_connect();} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {// 连接断开后重试esp_wifi_connect();xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);ESP_LOGW(TAG, "WiFi disconnect,try again...");} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {// 获取 IP 地址后标记连接成功ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;ESP_LOGI(TAG, "get IP address: " IPSTR, IP2STR(&event->ip_info.ip));xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);}
}void wifi_init_sta(void)
{// 1. 初始化事件组(用于同步 WiFi 连接状态)s_wifi_event_group = xEventGroupCreate();// 2. 初始化 esp-netif(替代旧的 tcpip_adapter_init())ESP_ERROR_CHECK(esp_netif_init());// 3. 创建默认事件循环(处理 WiFi 和 IP 事件)ESP_ERROR_CHECK(esp_event_loop_create_default());// 4. 创建默认的 WiFi Station 网络接口esp_netif_create_default_wifi_sta();// 5. 初始化 WiFi 驱动wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(&cfg));// 6. 注册事件处理函数(监听 WiFi 和 IP 事件)ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));// 7. 配置 WiFi 连接参数(SSID 和密码)wifi_config_t wifi_config = {.sta = {.ssid = WIFI_SSID,.password = WIFI_PASSWORD,},};ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));         // 设置为 Station 模式ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));  // 应用配置ESP_ERROR_CHECK(esp_wifi_start());                         // 启动 WiFiESP_LOGI(TAG, "WiFi init finish,connect %s...", WIFI_SSID);
}void app_main(void) {/////////////////////////////////////////////////////////////// 1. 初始化 NVS(存储 WiFi 配置等信息)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());  // 若 NVS 满或版本不兼容,先擦除ret = nvs_flash_init();}ESP_ERROR_CHECK(ret);// 2. 初始化 WiFi 并连接wifi_init_sta();// 3. 等待 WiFi 连接成功(阻塞直到获取 IP)xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT,pdFALSE, pdTRUE, portMAX_DELAY);///////////////////////////////////////////////////////////////// 初始化队列tx_queue = xQueueCreate(QUEUE_LEN, sizeof(DataPacket));rx_queue = xQueueCreate(QUEUE_LEN, sizeof(DataPacket));if (!tx_queue || !rx_queue) {printf("队列创建失败\n");return;}// 启动处理任务(一直运行)xTaskCreate(process_task, "process_task", 4096, NULL, 5, NULL);// 初始化Wi-Fi并连接(连接成功后调用tcp_connect)tcp_connect();  // Wi-Fi连接成功后调用//   vTaskDelay(pdMS_TO_TICKS(2000));  // 未连接则休眠
}

//接受网络消息失败就重连

3:服务器代码 go

package mainimport ("fmt""net" "time"
)func Process(conn net.Conn) {// 循环接收客户端发送的数据clientIp := conn.RemoteAddr().String() // 客户端IP:portdefer conn.Close()//先发送一条消息//fmt.Printf("hello client",)//_, err := conn.Write([]byte("hello client"))//if err != nil {//	return//}// 关闭connfor {// 创建一个新的切片buf := make([]byte, 1024)// fmt.Printf("服务器在等待客户端%s发送信息\n", conn.RemoteAddr().String())n, err := conn.Read(buf) // 从conn中读取// 等待客户端通过conn发送信息,// 如果客户端没有发送(write),就会阻塞在这里if err != nil {// 一般为这个errfmt.Printf("[%v]客户端%s已退出[%v]..\n", time.Now().Unix(), clientIp, err.Error())return}// 显示客户端发送的内容到服务器的终端fmt.Printf("%s: %s", clientIp, string(buf[:n])) // 读到了n个数据curtime := time.Now().Unix()time.Sleep(time.Second)conn.Write([]byte(fmt.Sprintf("server time=%v len=%v", curtime, n)))}
}func main() {fmt.Println("服务器开始监听...")// tcp表示使用的网络协议// 127.0.0.1:8888表示监听的IP:portlisten, err := net.Listen("tcp", "0.0.0.0:8888")if err != nil {fmt.Println("listen err =", err)return}defer listen.Close() // 延时关闭listenfmt.Println("listening success:", listen.Addr())// 循环等待客户端来连接fmt.Println("等待客户端来连接..")for {conn, err := listen.Accept()if err != nil {fmt.Println("Accept() err =", err)} else {fmt.Printf("[%v]客户端%s已连接..\n", time.Now().Unix(), conn.RemoteAddr().String())}// 准备一个协程,为客户端服务go Process(conn)}}

4: 测试结果 如果对你又帮助,麻烦点个赞,加个关注
在这里插入图片描述

http://www.dtcms.com/a/333251.html

相关文章:

  • Linux操作系统从入门到实战(二十二)命令行参数与环境变量
  • 信刻光盘摆渡系统案例——某省纪委
  • 微服务容错与监控体系设计
  • 生存主义:隐形异变 (Survivalist: Invisible Strain)免安装中文版
  • Leetcode 最小生成树系列(1)
  • 解决zabbix图片中文乱码
  • Mac(二)Homebrew 的安装和使用
  • 前端更改浏览器默认滚动条样式
  • 716SJBH高职院校财务收费系统的设计与实现
  • 25. 移动端-uni-app
  • 【论文解读】DDRNet:深度双分辨率网络在实时语义分割中的结构与原理全面剖析
  • LeetCode 905.按奇偶排序数组
  • 【机器学习深度学习】客观评估主观评估:落地场景权重比例
  • Rust 中 i32 与 *i32 的深度解析
  • 大华相机RTSP无法正常拉流问题分析与解决
  • flume实战:从零配置到启动运行的完整指南
  • 【Ubuntu 中安全删除 Windows 分区并优化磁盘空间利用】
  • redis-sentinel基础概念及部署
  • 机械学习---- PCA 降维深度解析
  • Vue响应式系统在超大型应用中的性能瓶颈
  • 深度学习实战115-基于Qwen3的多智能体协同深度数据分析:架构、流程与实现
  • 光伏工单智能管理,故障处理快人一步
  • 金融业务安全增强方案:国密SM4/SM3加密+硬件加密机HSM+动态密钥管理+ShardingSphere加密
  • 不同DuckDB插件对不同工具生成的xlsx文件读取速度的比较
  • sqlsever的sql转postgresql的sql的方言差异
  • 日本CN2服务器租用多少钱
  • Linux -- 线程概念与控制
  • Spring Boot 静态函数无法自动注入 Bean?深入解析与解决方案
  • 死锁总结及解决方案
  • MetaFox官方版:轻松转换视频,畅享MKV格式的便捷与高效