乐鑫Esp32学习之旅⑨ esp32上实现本地 TCP 客户端和服务端角色,可断线重连原路返回数据。(附带Demo)


本篇博文目录:


一. 前言;


  • 2018年的高考分数线出来了,广东省的本科的理科分数线 375分,貌似比当年2014年高考低好多啊。。哈哈!

  • 回归正题,TCP基本协议,我就不多说了,可以看看我的前面的8266系列的,本博文通讯框架基于lwip,主要修改实现以下功能:

    • TCP服务端时候,客户端连接进来的数据原路返回,而且断开连接之后,允许重新连接。
    • TCP客户端时候,设置每三秒向服务器发送数据!

二. 服务端;


  • 左边是 visual studio code编译器的终端显示esp32显示打印的数据,右边是手机通讯调试助手。
    这里写图片描述

2.1 实现的过程:

  • 首先根据配置信息来开启热点模式,等待设备的连接。
  • 设备连接成功后,开启TCP server服务器端,然后通讯!
  • 注意的是:当前的网关的IP地址就是服务器地址,默认是192.168.4.1这个是乐鑫在出厂时候写死的!知道就好!

2.2 核心的代码:


  • ①:创建热点(和上篇的UDP一样):
void wifi_init_softap()
{
    tcp_event_group = xEventGroupCreate();

    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = SOFT_AP_SSID, //设置名字
            .ssid_len = 0,
            .max_connection = SOFT_AP_MAX_CONNECT, //最大连接数目
            .password = SOFT_AP_PAS, //密码
            .authmode = WIFI_AUTH_WPA_WPA2_PSK},//加密方式
    };
    if (strlen(EXAMPLE_DEFAULT_PWD) == 0)//如果密码为空,则设置开放性wifi热点
    {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN; 
    }

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

    ESP_LOGI(TAG, "SoftAP set finish:%s pas:%s \n",EXAMPLE_DEFAULT_SSID, EXAMPLE_DEFAULT_PWD);
}

  • ②:创建TCP服务器,等待客户端连接:

//形参的含义:true为创建服务器并且等待连接,false仅仅只是等待连接;返回值是是否成功接收到一个设备的连接
esp_err_t create_tcp_server(bool isCreatServer)
{

    if (isCreatServer)
    {
        ESP_LOGI(TAG, "server socket....,port=%d\n", TCP_PORT);
        server_socket = socket(AF_INET, SOCK_STREAM, 0);

        if (server_socket < 0)
        {
            show_socket_error_reason("create_server", server_socket);
            return ESP_FAIL;
        }

        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(TCP_PORT);//指定的端口号
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

        //开始创建
        if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
        {
            show_socket_error_reason("bind_server", server_socket);
            close(server_socket);
            return ESP_FAIL;
        }
    }

    //监听指定的端口
    if (listen(server_socket, 5) < 0)
    {
        show_socket_error_reason("listen_server", server_socket);
        close(server_socket);
        return ESP_FAIL;
    }

    connect_socket = accept(server_socket, (struct sockaddr *)&client_addr, &socklen);
    //判断是否连接成功
    if (connect_socket < 0)
    {
        show_socket_error_reason("accept_server", connect_socket);
        close(server_socket);
        return ESP_FAIL;
    }

    /*connection established,now can send/recv*/
    ESP_LOGI(TAG, "tcp connection established!");
    return ESP_OK;
}

  • ③:接收数据:
void recv_data(void *pvParameters)
{
    int len = 0;
    char databuff[1024];
    while (1)
    {
     while (1){  
            //每次接收都要清空接收数组
            memset(databuff, 0x00, sizeof(databuff));
            len = recv(connect_socket, databuff, sizeof(databuff), 0);
            //打印接收到的数组
            ESP_LOGI(TAG, "recvData: %s\n", databuff);
            //原路返回,不指定某个客户端
            send(connect_socket, databuff , sizeof(databuff), 0);
        if (len > 0) {
                g_total_data += len;
                g_rxtx_need_restart = false;
            } else {
                show_socket_error_reason("recv_data", connect_socket);
                g_rxtx_need_restart = true;
            }
        }
    }
    g_rxtx_need_restart = true;
    vTaskDelete(NULL);
}

2.3 注意问题:


  • 虽然乐鑫也提供了TCP的例程,但是发现是当客户端断开连接之后再次连接时候,客户端提示已经连接成功,但发现服务器并没有去监听这个设备的信息,原因在于没有去等待监听,还是要通过listen()accept()方法来获取一个远程的设备地址。

三. TCP Client客户端;


  • 下图可以看到,服务器的地址为192.168.1.104,端口:8266 ,此配置信息可在demo里面的头文件修改!

  • 发送数据原路返回和上面的服务器代码一样,只是当与服务器断开连接时候,需要关闭socket重新连接,而我们的服务器是不需要关闭socket只是继续等待客户端连接,这一点需要注意!

这里写图片描述


3.1 实现的过程:

  1. 上电开启station模式来连接指定的路由器,连接成功之后创建socket连接tcp server服务器。
  2. 连接成功后,需要开启收发的任务,并且在收到数据之后原路返回。
  3. 如果连接成功之后,服务器突然断开,那么esp32需要断开socket,毕竟服务器已经断开,留着也没用,之后尝试重新创建socket来连接服务器,直到成功为止!

3.2 核心代码:

  • ① 连接指定的路由器:
void wifi_init_sta()
{
    tcp_event_group = xEventGroupCreate();

    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    wifi_config_t wifi_config = {
            .sta = {
            .ssid = GATEWAY_SSID, //路由器名字
            .password = GATEWAY_PAS},//路由器密码
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_LOGI(TAG, "wifi_init_sta finished.");
    ESP_LOGI(TAG, "connect to ap SSID:%s password:%s \n",
             GATEWAY_SSID, GATEWAY_PAS);
}
  • ② 创建客户端并且指定连接到服务器端:
//创建TCP客户端连接到指定的服务器
esp_err_t create_tcp_client()
{

    ESP_LOGI(TAG, "will connect gateway ssid : %s port:%d\n",
             TCP_SERVER_ADRESS, TCP_PORT);

    connect_socket = socket(AF_INET, SOCK_STREAM, 0);

    if (connect_socket < 0)
    {
        show_socket_error_reason("create client", connect_socket);
        return ESP_FAIL;
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(TCP_PORT);
    server_addr.sin_addr.s_addr = inet_addr(TCP_SERVER_ADRESS);
    ESP_LOGI(TAG, "connectting server...");
    if (connect(connect_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        show_socket_error_reason("client connect", connect_socket);
        ESP_LOGE(TAG, "connect failed!");
        return ESP_FAIL;
    }
    ESP_LOGI(TAG, "connect success!");
    return ESP_OK;
}

demo的配置信息:

  • 下面的配置宏定义需要在头文件修改!
//true为开启热点并且创建tcp服务器,fasle为连接到指定的路由器并且连接到指定的tcp服务器
#define TCP_SERVER_CLIENT_OPTION FALSE 
//打印的tag
#define TAG "XuHongTCP-->" 

//以下是softAP热点模式的配置信息
#define SOFT_AP_SSID "XuHongTCP2018"
#define SOFT_AP_PAS "xuhong123456" //如果密码设置为空,则配置的热点是开放的,没有密码的。
#define SOFT_AP_MAX_CONNECT 1 //作为AP热点时候,最大的连接数目


//以下是station模式配置信息,是您家里的路由器的信息
#define GATEWAY_SSID "AliyunOnlyTest"
#define GATEWAY_PAS "aliyun#123456"
#define TCP_SERVER_ADRESS "192.168.1.104" //要连接TCP服务器地址
//统一的端口号,包括TCP客户端或者服务端
#define TCP_PORT 8266

五 下载:


猜你喜欢

转载自blog.csdn.net/xh870189248/article/details/80817529