ESP-C3入门10. 创建TCP Client

news2024/9/28 21:23:16

ESP-C3入门10. 创建TCP Client

  • 一、创建 tcp client的一般步骤
    • 1. 创建 tcp 套接字
    • 2. 配置服务器地址
    • 3. 连接服务器
    • 4. 发送数据
    • 5. 接收数据
    • 6. 关闭套接字
  • 二、创建tcp_client任务
  • 三、示例代码
    • 1. tcpClient.h
    • 2. tcpClient.c
    • 3. main.c

在这里插入图片描述

一、创建 tcp client的一般步骤

本文示例使用的是阻塞IO进行网络通讯。 在高性能的网络编程中,可能会使用非阻塞IO或异步IO进行网络通讯。

1. 创建 tcp 套接字

使用 socket函数:

int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

2. 配置服务器地址

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(server_port);
inet_pton(AF_INET, server_ip, &server_addr.sin_addr);

3. 连接服务器

int ret = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret != 0) {
    ESP_LOGE(TAG, "Failed to connect to server, errno=%d", errno);
    close(sock);
    return;
}

4. 发送数据


int bytes_sent = send(sock, send_buf, send_len, 0);
if (bytes_sent < 0) {
    ESP_LOGE(TAG, "Failed to send data to server, errno=%d", errno);
    close(sock);
    return;
}

5. 接收数据

// 接收数据
int bytes_recv = recv(sock, recv_buf, recv_buf_size, 0);
if (bytes_recv < 0) {
    ESP_LOGE(TAG, "Failed to receive data from server, errno=%d", errno);
    close(sock);
    return;
}

6. 关闭套接字

close(sock);

二、创建tcp_client任务

由于网络通信涉及到阻塞IO操作,如果在主线程中直接调用网络API,会导致主线程被阻塞,无法处理其他任务。为了避免这种情况发生,可以将网络通信放在RTOS任务中处理,使得主线程可以继续运行。
另外,使用RTOS任务的好处还在于可以方便地控制任务的优先级、堆栈大小等参数,以及在需要的时候暂停、恢复、删除任务等操作。这样可以更加灵活地管理程序中的各个任务,实现复杂的多任务协作。
示例使用 xTaskCreate函数创建freeRTOS的任务:

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

任务优先级是5,栈大小是4096

定义连接tcp 的任务时,注意需要保留参数:

void tcp_client_task(void* pvParameters);

三、示例代码

本示例会接前面章节连接WIFI的部分,帮wifi.c wifi.h部分代码不重复编写 。
下面示例中,tcpClient连接上服务器后,会发送ping消息,并等待服务器回应。如果服务器回应pong,则握手成功, 客户端继续发送10个报文, 然后关闭连接,重新请求连接服务端。

参考项目目录结构如下:
在这里插入图片描述

1. tcpClient.h

//
// Created by hs26661 on 2023/2/16.
//

#ifndef ESP32_LEARN_TCPCLIENT_H
#define ESP32_LEARN_TCPCLIENT_H

#include <string.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
void tcp_client_task(void* pvParameters);


#endif //ESP32_LEARN_TCPCLIENT_H

2. tcpClient.c

//
// Created by hs26661 on 2023/2/16.
//
#include <lwip/sockets.h>
#include <esp_log.h>

#define SERVER_HOST "192.18.200.28"
#define SERVER_PORT 3000
#define MESSAGE "ping"
#define MAX_RETRY 5 // 最大重试次数
#define DATA_SIZE 256 // 数据包大小

static const char *TAG = "TCP_CLIENT";

/**
 * 创建 freeRtos任务, 这里的参数注意不能删除
 * @param pvParameters
 */
void tcp_client_task(void* pvParameters){
    // 重试次数
    int retry_count = 0;
    // 接收缓冲区
    char rx_buffer[DATA_SIZE];
    // 发送缓冲区
    char tx_buffer[DATA_SIZE];

    while(1){
        // 创建套接字
        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr(SERVER_HOST);
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(SERVER_PORT);
        int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);

        // 连接服务端
        int err = connect(sock, (struct sockaddr*) &dest_addr, sizeof(dest_addr));
        if(err != 0){
            if(retry_count++ >= MAX_RETRY){
                ESP_LOGE(TAG, "connect failure retry limit");
                break;
            }
            ESP_LOGW(TAG, "connect failure, retry count=%d", retry_count);
            vTaskDelay(2000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "connect to server success");
        retry_count = 0;

        // 发送ping
        int tx_len = sprintf(tx_buffer, MESSAGE);
        err = send(sock, tx_buffer, tx_len, 0);
        if(err <0){
            ESP_LOGE(TAG, "send ping failure");
            close(sock);
            continue;
        }
        // 接收pong
        int rx_len = recv(sock,rx_buffer, sizeof(rx_buffer) -1, 0);
        if(rx_len<0){
            ESP_LOGE(TAG, "receive pong failure");
            close(sock);
            continue;
        }
        rx_buffer[rx_len] = 0;
        if(strcmp(rx_buffer, "pong") !=0){
            ESP_LOGE(TAG, "handshake failure");
            close(sock);
            continue;
        }
        ESP_LOGI(TAG, "handshake success");
        // 发送数据包
        for(int i =0;i<10;i++){
            // 获取当前时间字符串
            time_t now = time(NULL);
            struct tm timeinfo;
            localtime_r(&now, &timeinfo);
            char strftime_buf[64];
            strftime(strftime_buf , sizeof(strftime_buf), "%c", &timeinfo);
            // 发送数据包
            tx_len = sprintf(tx_buffer, "time:%s", strftime_buf);
            err = send(sock, tx_buffer, tx_len, 0);
            if(err <0){
                ESP_LOGE(TAG, "send data error");
                close(sock);
                break;
            }
            ESP_LOGI(TAG, "send data %d success", i+1);
            // 5秒后再发送
            vTaskDelay(5000 / portTICK_PERIOD_MS);
        }
        // 发送bye
        tx_len = sprintf(tx_buffer, "bye");
        err = send(sock, tx_buffer, tx_len, 0);
        if(err<0){
            ESP_LOGE(TAG, "send bye error");
            close(sock);
            continue;
        }
        // 关闭连接
        close(sock);
        ESP_LOGI(TAG, "connection closed");

        // 重建 连接
        ESP_LOGI(TAG, "reconnect");
        vTaskDelay(2000/ portTICK_PERIOD_MS);

    }
    vTaskDelete(NULL);
}

3. main.c

#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

#include <nvs_flash.h>
#include "network/include/wifi.h"
#include "network/include/tcpClient.h"

static const char *TAG = "wifi connection";

void app_main()
{
    int i = 0;
    ESP_LOGE(TAG, "app_main");
    // 初始化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);

    // Wi-Fi初始化
    ESP_LOGI(TAG, "Wi-Fi initialization");
    wifi_initialize();

    // Wi-Fi Station初始化
    wifi_station_initialize();

    // 创建 tcp client任务,优先级为5,栈大小为4096
    xTaskCreate(tcp_client_task, "tcp_client_task", 4096, NULL, 5, NULL);

        while (1) {
            vTaskDelay(pdMS_TO_TICKS(500));
        }
}

运行结果:
在这里插入图片描述
示例中使用阻塞IO通讯,也可以将收发放在不同的任务中执行,这样提高程序并发性;在这种情况下,可以创建两个套接字并使用同一服务器地址和端口号。在发送和接收数据时,将不同的套接字分别用于不同的任务,从而实现并发的发送和接收。

具体来说,在两个任务中,需要使用不同的套接字句柄(socket handle)来访问相同的服务器地址和端口。在每个任务中,可以通过调用 socket()、connect() 和 close() 等函数来创建和管理套接字。发送和接收数据时,需要在每个任务中使用不同的套接字句柄来分别发送和接收数据。

需要注意的是,如果两个任务共用同一套接字句柄来进行发送和接收,那么就可能会出现竞争条件,导致数据错误或不完整。因此,为了避免这种情况,最好在每个任务中分别使用独立的套接字来进行通信。

本文不演示上述方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/349340.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Redis】Redis 常用数据类型操作 ① ( 数据库操作 | Redis 数据库连接参数 | Redis 数据库个数 | Redis 访问机制 )

文章目录一、Redis 数据库连接参数二、Redis 数据库个数三、Redis 访问机制一、Redis 数据库连接参数 连接 Redis 数据库 , 只需要 IP 地址 , 端口号 , 访问密码 即可 , 如果没有 设置 访问密码 可忽略该选项 ; Redis 默认端口号是 6379 ; 参考 【Redis】Redis 数据库 安装、…

【C语言学习笔记】:折叠表达式

在C中通过折叠表达式可以更容易的在可变参数模板中处理可变参数。 先看一下传统的使用递归方式来处理可变参数的代码&#xff1a; #include <iostream>void print(int i) { std::cout << i << " ";}void func() {}template<typename T1, t…

C++为什么能重夺年度语言?

目录一、爷青回1、年初依旧很多大新闻&#xff0c;其中一条就是TIOBE把年度编程语言颁给了C。2、这是什么概念&#xff1f;那一年Java的流行指数是14%。二、C为什么衰落三、C为什么重新流行1、C为什么重新流行起来了呢&#xff1f;2、C究竟做对了什么呢&#xff1f;3、根本原因…

Python入门自学进阶-Web框架——31、开发客户报名流程

完成客户报名的流程流程大体如下&#xff1a;在已有收集的客户信息基础上——>销售填写报名表&#xff08;报什么班、课程顾问&#xff09;——>自动生成一个链接&#xff0c;让学员填写——>学员填写个人信息&#xff0c;并上传身份照片&#xff0c;同意合同协议——…

墨天轮【第二届数据库掌门人论坛】圆满收官 | 含嘉宾精彩观点回顾

2月10日上午&#xff0c;墨天轮【2023春季发布会暨第二届数据库掌门人论坛】盛大开启&#xff0c;本次活动的主题为“新征程&#xff0c;向未来”&#xff0c;共包含2022年度中国数据库颁奖盛典、2022年度行业发展报告发布以及第二届数据库掌门人论坛三项议程。华为云数据库服务…

win10-右键打开windows terminal

文章目录windows terminal设置右键打开打开注册表添加一个右键选项新建一个项添加右键的名称和图标右键选项执行的命令测试windows terminal windows 新一代命命令行 设置右键打开 打开注册表 WinR 输入&#xff1a; regedit 定位&#xff1a; 计算机\HKEY_CLASSES_ROOT\Di…

中国第一份ERP系统用户实名口碑选型报告(选型宝重磅发布!)

01 报告目录 一、 报告概况 二、 ERP类厂商-用户实名口碑排行 三、 ERP类产品-用户实名口碑对比 四、 主流ERP产品简介 五、 ERP常用功能 六、 算法说明 七、 联系选型宝 02 报告概述 一、报告亮点 这是一份完全由用户实名点评生成的ERP系统选型报告。 报告由选…

关于 Eclipse 的一场 “三角关系”

上个世纪 90 年代&#xff0c;世界上的计算机要么不联网&#xff0c;要么在企业内部联网。但是&#xff0c;在互联网的概念下&#xff0c;计算机之间共享信息和资源的需求成为了必要。 1995 年 5 月&#xff0c;Java 横空出世。Java 的父亲是当时凭借 Solaris 操作系统风头正盛…

2023-02-16 学习记录--React-邂逅Redux(三)

React-邂逅Redux&#xff08;三&#xff09; “天道酬勤&#xff0c;与君共勉”——承接React-邂逅Redux&#xff08;二&#xff09;&#xff0c;让我们一起继续探索Redux的奥秘吧~☺️【今日新知识——异步action】 一、什么是异步action&#xff1f; action有两种形式&#x…

C#:Krypton控件使用方法详解(第二讲) ——kryptonCheckBox

今天介绍的Krypton控件中的kryptonCheckBox&#xff0c;这个控件和VS中带的CheckBox控件还是不一样的&#xff0c;下面介绍这个控件。kryptonCheckBox控件的外观属性如下图所示&#xff1a;Checked属性&#xff1a;对应的属性值为Bool类型有两个&#xff0c;当属性值为true时kr…

DHCP协议

DHCP协议 文章目录DHCP协议DHCP作用及特点DHCP服务IP分配的三种方式DHCP协议中的报文类型DHCP服务工作流程抓包参考动态主机配置协议 DHCP&#xff08;Dynamic Host Configuration Protocol&#xff09;&#xff0c;提供了一种 插网即用的技术。DHCP是一个应用层协议。当我们将…

CANoe仿真工程开发

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、网络拓扑图二、数据库文件设计四、Panel设计五、CAPL代码实现六、Automation Sequences七、分析窗口设置八、Trace窗口与Logging九、Desktop布局十、工程运行…

【算法 | 例题简答】相关例题讲解

目录 简答题 计算题 时间复杂度的计算 递归算法计算 背包问题&#xff08;0-1背包问题&#xff09; 回溯法 动态规划法 编程题 用回溯法解方程 动态规划法解决蜘蛛吃蚊子 用分治法解决抛硬币问题 用二分法分两边求最大值 简答题 1、什么是算法&#xff1f;算法有哪…

Revit问题:Navisworks中导入的rvt模型角度不正确调整

一、Navisworks中导入的rvt模型角度不正确调整方法 通常情况下&#xff0c;我们做好一个Revit模型&#xff0c;有时候出于成果保护或者鉴于Revit自带的碰撞检测效果不够直观、Revit模型体量太大&#xff0c;需要一个轻量化的模型展示&#xff0c;我们通常情况下会使用Autodesk公…

【python】基于Socket的聊天室Python开发

基于Socket的聊天室Python开发一、Socket简述二、创建服务端Server2.1 创建服务端初始化2.2 监听客户端连接2.3 处理客户端消息三、创建客户端Client3.1 创建服务端初始化3.2 发送消息3.3 接收消息3.3 线程工作3.4 线程工作是不是挺好玩的呢&#xff1f;也可以作为课程设计哦&a…

【C++】Expression的学习笔记

关于不同类别表达式的举例&#xff0c;请参考博文《C 中的值类别》 1. 左值和右值的简单理解 左值对应了具有内存地址的对象&#xff0c;而右值仅仅是临时使用的值对象。&#xff08;引用自博文《C 中的值类别》&#xff09;左值有名称&#xff08;变量或常量名称&#xff09…

RFC7519规范-JWT - json web token

简介 什么是JWT(JSON Web Token) 在介绍JWT之前&#xff0c;我们先来回顾一下利用token进行用户身份验证的流程&#xff1a; 客户端使用用户名和密码请求登录服务端收到请求&#xff0c;验证用户名和密码验证成功后&#xff0c;服务端会签发一个token&#xff0c;再把这个to…

mybatis-spring-boot-starter中设定多数据源,并实现Durid的监控

实现功能&#xff1a; 1&#xff1a; 配置两个数据源&#xff0c;根据不同的mapper路径使用不同的数据连接 2 &#xff1a;使用Druid连接池 3&#xff1a;Druid 可监控多个数据源的sql执行操作 分析&#xff1a; 查看mybatis_spring_boot_stater中关于自动装配的类可知【如上…

基于动力学模型的无人驾驶车辆主动转向控制

章五 基于动力学模型的无人驾驶车辆主动转向控制 主动转向控制联合仿真 基于动力学模型的无人驾驶车辆主动转向控制 无人驾驶车辆模型预测控制第五章&#xff08;上&#xff09; 无人驾驶车辆模型预测控制第五章&#xff08;下&#xff09; 北理工无人驾驶车辆模型预测控制第4…

测试新版Android Studio的手机镜像效果

学更好的别人&#xff0c; 做更好的自己。 ——《微卡智享》 本文长度为669字&#xff0c;预计阅读2分钟 前言 春节刚上班&#xff0c;就开始了疯狂出差的节奏&#xff0c;期间发现Android Studio发布新的版本2022.1.1(Electric Eel)&#xff0c;里面两个更新的内容蓝牙模拟器和…