ESP32 TCP_Client调试

本文介绍如何使用VSCODE和ESP-IDF搭建ESP32的TCP客户端,并通过示例代码展示TCP通信的过程,包括连接建立、数据发送及速度测量。

目录

前言

一、新建工程

二、修改代码

三、测试


前言

使用VSCODE+ESP-IDF 实现ESP32的TCP客户端和TCP服务端通信

一、新建工程

配置ESP32连接的网络 

二、修改代码

修改tcp_client.c的代码如下

/* 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"

/*注意修改这里TCP连接的IP地址和端口号*/
#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 = "hello world\r\n";


typedef struct 
{       
    unsigned char TCP_ClientTag[1024 * 100];     
    // unsigned char TCP_DataBuffer[50];
    char *pData;
} _TCP_Data ;
_TCP_Data TCP_Data;
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) {

        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;

        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");
        // int err = 0;
        static unsigned int SendDataCnt = 0;
        static unsigned int Time1 = 0;
        static unsigned int Time2 = 0;
        while (1) {
            
            unsigned int LocalUserID = 1;
            unsigned int FarUserID   = 2;
            unsigned int DataLen     = 1024 * 40;
            
            TCP_Data.TCP_ClientTag[0] = 0xA1;
            TCP_Data.TCP_ClientTag[1] = 0xA2;

            TCP_Data.TCP_ClientTag[2] = LocalUserID;
            TCP_Data.TCP_ClientTag[3] = LocalUserID>>8;
            TCP_Data.TCP_ClientTag[4] = LocalUserID>>16;
            TCP_Data.TCP_ClientTag[5] = LocalUserID>>24;

            TCP_Data.TCP_ClientTag[6] = FarUserID;
            TCP_Data.TCP_ClientTag[7] = FarUserID>>8;
            TCP_Data.TCP_ClientTag[8] = FarUserID>>16;
            TCP_Data.TCP_ClientTag[9] = FarUserID>>24;

            TCP_Data.TCP_ClientTag[10] = DataLen;
            TCP_Data.TCP_ClientTag[11] = DataLen>>8;
            TCP_Data.TCP_ClientTag[12] = DataLen>>16;
            TCP_Data.TCP_ClientTag[13] = DataLen>>24;

            TCP_Data.TCP_ClientTag[14] = 0xA3;
            TCP_Data.TCP_ClientTag[15] = 0xA4;
            // strcpy(&TCP_Data.TCP_DataBuffer,payload);
            err = send(sock,&TCP_Data,1024 * 100, 0);
            //send(sock,payload,strlen(payload), 0);
            if (err < 0) {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }
            Time2 = esp_log_timestamp();
            SendDataCnt+=err;
            if((Time2 - Time1) > 1000)
            {
                float TCP_Speed = ((float)SendDataCnt)/((float)(Time2 - Time1)/1000.0f); /*bytes/s*/
                ESP_LOGI(TAG,"TCP speed = %f kb/s\r\n",TCP_Speed / 1024.0f);

                Time1 = Time2;
                SendDataCnt = 0;
            }
            // 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(1);//1 / 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);
}

编译下载后

 

三、测试

随便下载一个TCP/UDP网络助手TCP/UDP网络助手

 

需要注意的是,买的模块质量参差不齐,距离远的时候可能会连接不到WIFI,或者通信的速度非常的慢!巨坑!!!

### 常见错误分析:`esp_transport_read failed ESP_ERR_ESP_TLS_TCP_CLOSED_FIN` 在使用 ESP-IDF 的 WebSocket 客户端时,如果日志中出现 `esp_transport_read failed: ESP_ERR_ESP_TLS_TCP_CLOSED_FIN` 错误,通常表示底层 TCP 或 TLS 连接已被远程服务器关闭。该错误表明 ESP32 无法从连接中读取数据,因为对方已经发送了 FIN 包以终止连接。 这种问题可能由多种原因引起,包括但不限于: - **服务器主动关闭连接**:WebSocket 服务器可能因超时、协议错误或负载限制等原因主动断开连接。 - **TLS 握手失败或证书验证异常**:若使用 `wss://` 加密连接,SSL/TLS 层的任何异常都可能导致连接提前终止[^1]。 - **网络中断或不稳定**:Wi-Fi 信号弱、路由器限制连接时间或 DNS 解析失败等都可能引发此错误。 - **心跳机制未正确配置**:缺少 PING/PONG 心跳包维持连接活跃状态,导致服务器认为连接失效并关闭连接。 --- ### 调试与解决方法 #### 检查 WebSocket 握手过程 确保客户端发送的 HTTP 升级请求符合规范,并包含正确的 `Sec-WebSocket-Key` 和 `Upgrade` 头部[^2]。ESP-IDF 默认会自动生成 `Sec-WebSocket-Key`,但手动设置时需确保其为 16 字节随机数据的 Base64 编码。 ```c static const char *REQUEST = "GET /ws HTTP/1.1\r\n" "Host: example.com\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Version: 13\r\n" "\r\n"; ``` #### 验证 TLS 配置(适用于加密连接) 使用 `esp_websocket_client_get_ssl_conn_state()` 可以检查 SSL/TLS 层的状态,确认是否握手成功或是否存在证书验证错误[^1]。 ```c esp_tls_conn_state_t state = esp_websocket_client_get_ssl_conn_state(client); ESP_LOGI(TAG, "SSL connection state: %d", state); ``` 此外,确保证书路径正确且格式支持 PEM 编码: ```c config.cert_pem = (const char *)server_cert_pem_start; ``` #### 网络稳定性与重连机制 ESP-IDF 的 WebSocket 客户端库不自动重连,建议在事件回调中检测 `WEBSOCKET_EVENT_DISCONNECTED` 并实现重连逻辑: ```c case WEBSOCKET_EVENT_DISCONNECTED: ESP_LOGW(TAG, "WebSocket disconnected, attempting reconnect..."); esp_websocket_client_reconnect(client); break; ``` #### 启用心跳机制维持连接 合理设置 PING 发送间隔可以防止服务器因超时而断开连接: ```c config.websocket.ping_interval_sec = 30; // 每30秒发送一次PING ``` 同时确保自动响应 PING 消息,或在事件处理中手动发送 PONG: ```c case WEBSOCKET_EVENT_PING: ESP_LOGD(TAG, "Received PING, responding with PONG automatically."); break; ``` #### 日志调试与错误追踪 启用详细日志输出有助于定位具体错误来源: ```c esp_log_level_set("WEBSOCKET_CLIENT", ESP_LOG_DEBUG); esp_log_level_set("TRANS_SSL", ESP_LOG_DEBUG); ``` 通过日志可观察到 SSL 握手失败、TCP 断开、DNS 解析错误等关键信息。 --- ### 总结 当 ESP-IDF WebSocket 客户端报告 `esp_transport_read failed ESP_ERR_ESP_TLS_TCP_CLOSED_FIN` 错误时,应重点排查以下方面: - WebSocket 握手请求是否完整且符合 RFC 6455 规范。 - TLS/SSL 配置是否正确,证书是否有效。 - 网络连接是否稳定,是否存在 DNS 或 Wi-Fi 故障。 - 是否启用了适当的心跳机制以维持连接活跃。 - 是否实现了自动重连逻辑以应对临时性断开。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值