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

STM32 实现 TCP 服务器与多个设备通信

目录

一、引言

二、硬件准备

三、软件准备

四、LWIP 协议栈的配置与初始化

五、创建 TCP 服务器

1.创建 TCP 控制块

2.绑定端口 

3. 进入监听状态

4.设置接收回调函数 

六、处理多个客户端连接 

七、数据处理与通信管理 

八、错误处理与资源管理 

九、总结 

 


一、引言

        在嵌入式系统开发中,常常需要实现设备之间的网络通信。STM32 作为一款广泛应用的微控制器,结合网络通信功能可以实现与多个设备的交互。本文将介绍如何在 STM32 上实现 TCP 服务器端,以便与多个设备进行通讯。

二、硬件准备

  1. STM32 开发板:选择一款带有以太网接口的 STM32 开发板,如 STM32F429 系列等。
  2. 以太网模块(可选):如果开发板没有内置以太网接口,可以选择一个外接的以太网模块,如 W5500 等。
  3. 网络连接:将开发板连接到同一局域网内,确保其他设备可以通过网络访问到 STM32 开发板。

三、软件准备

  1. 开发环境:如 Keil MDK、IAR Embedded Workbench 等。
  2. LWIP(Lightweight IP)协议栈:LWIP 是一个轻量级的 TCP/IP 协议栈,适用于嵌入式系统。可以从 LWIP 官方网站下载并集成到开发环境中。
  3. STM32 库:使用 STM32 的官方库或者其他第三方库来进行硬件驱动和开发。

四、LWIP 协议栈的配置与初始化

  1. 将 LWIP 协议栈的源文件添加到 STM32 项目中,并设置正确的编译选项。
  2. 在项目的初始化代码中,调用 LWIP 的初始化函数lwip_init(),完成协议栈的初始化。
  3. 根据实际需求,配置 LWIP 的参数,如 IP 地址、子网掩码、默认网关等。可以通过修改lwipopts.h文件来实现。

五、创建 TCP 服务器

1.创建 TCP 控制块

使用 LWIP 的 API 函数tcp_new()创建一个新的 TCP 控制块。

  struct tcp_pcb *tcp_server_pcb;tcp_server_pcb = tcp_new();

2.绑定端口 

使用tcp_bind()函数将 TCP 控制块绑定到一个特定的端口号。通常选择一个未被其他应用程序占用的端口。

   err_t err = tcp_bind(tcp_server_pcb, IP_ADDR_ANY, SERVER_PORT);if (err!= ERR_OK) {// 处理绑定失败的情况}

这里的SERVER_PORT是服务器监听的端口号。

3. 进入监听状态

使用tcp_listen()函数使 TCP 控制块进入监听状态,等待客户端的连接请求。

 tcp_server_pcb = tcp_listen(tcp_server_pcb);

4.设置接收回调函数 

使用tcp_accept()函数设置一个接收回调函数,当有客户端连接请求时,该回调函数将被调用。

tcp_accept(tcp_server_pcb, tcp_accept_callback);

tcp_accept_callback是自定义的回调函数,用于处理客户端的连接请求。

六、处理多个客户端连接 

1.在接收回调函数中,接受客户端的连接请求,并为每个连接创建一个新的 TCP 控制块来处理与该客户端的通信。

void tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err){if (err == ERR_OK) {// 处理新的连接// 可以为每个连接分配一些资源,如缓冲区等tcp_recv(newpcb, tcp_receive_callback, NULL);}}

 

2.为每个连接设置接收回调函数,以便在有数据到达时进行处理。

void tcp_receive_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err){if (err == ERR_OK && p!= NULL) {// 处理接收到的数据// 可以根据需要将数据转发给其他连接或者进行其他处理tcp_write(tpcb, p->payload, p->len, TCP_WRITE_FLAG_COPY);// 释放接收到的数据包缓冲区pbuf_free(p);}}

3.使用数据结构管理多个连接 

可以使用链表、数组等数据结构来管理多个客户端连接。例如,可以创建一个链表,每个节点存储一个客户端连接的信息,包括 TCP 控制块、客户端地址等。

typedef struct _client_connection{struct tcp_pcb *pcb;ip_addr_t client_ip;u16_t client_port;struct _client_connection *next;} client_connection_t;client_connection_t *client_list = NULL;

在连接建立时,将新的连接添加到链表中;在连接关闭时,从链表中删除相应的节点。

void add_client_connection(struct tcp_pcb *pcb, const ip_addr_t *client_ip, u16_t client_port){client_connection_t *new_connection = (client_connection_t *)malloc(sizeof(client_connection_t));new_connection->pcb = pcb;new_connection->client_ip = *client_ip;new_connection->client_port = client_port;new_connection->next = client_list;client_list = new_connection;}void remove_client_connection(struct tcp_pcb *pcb){client_connection_t *prev = NULL;client_connection_t *current = client_list;while (current!= NULL) {if (current->pcb == pcb) {if (prev == NULL) {client_list = current->next;} else {prev->next = current->next;}free(current);break;}prev = current;current = current->next;}}

七、数据处理与通信管理 

1.在接收回调函数中,可以根据接收到的数据进行相应的处理。例如,可以解析数据、执行特定的操作或者将数据转发给其他连接。

void tcp_receive_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err){if (err == ERR_OK && p!= NULL) {// 处理接收到的数据char *data = (char *)p->payload;// 根据数据内容进行处理// 可以将数据转发给其他连接client_connection_t *current = client_list;while (current!= NULL) {if (current->pcb!= tpcb) {tcp_write(current->pcb, data, strlen(data), TCP_WRITE_FLAG_COPY);}current = current->next;}// 释放接收到的数据包缓冲区pbuf_free(p);}}

2.如果需要向特定的客户端发送数据,可以遍历链表找到相应的客户端连接,并使用tcp_write函数进行数据发送。

void send_data_to_client(const ip_addr_t *client_ip, u16_t client_port, char *data){client_connection_t *current = client_list;while (current!= NULL) {if (ip_addr_cmp(&current->client_ip, client_ip) && current->client_port == client_port) {tcp_write(current->pcb, data, strlen(data), TCP_WRITE_FLAG_COPY);break;}current = current->next;}}

八、错误处理与资源管理 

1.在整个通信过程中,需要进行适当的错误处理。例如,当连接失败、数据发送失败或接收超时等情况发生时,需要采取相应的措施,如重新连接、报告错误或释放资源等。

void tcp_error_callback(void *arg, err_t err){// 处理错误情况struct tcp_pcb *tpcb = (struct tcp_pcb *)arg;remove_client_connection(tpcb);}

2.当连接关闭时,需要及时释放相关的资源,如缓冲区、TCP 控制块等,以避免内存泄漏。

 void tcp_close_callback(void *arg, err_t err){// 处理连接关闭情况struct tcp_pcb *tpcb = (struct tcp_pcb *)arg;remove_client_connection(tpcb);}

九、总结 

通过以上步骤,我们可以在 STM32 上实现一个 TCP 服务器,与多个设备进行通信。在实际应用中,可以根据具体的需求进行进一步的优化和扩展,例如添加安全认证、数据加密、流量控制等功能。同时,还需要注意网络稳定性和可靠性,以确保通信的正常进行。

希望本文对大家在 STM32 上实现 TCP 服务器与多个设备通信有所帮助。如果有任何问题或建议,欢迎在评论区留言交流。

 

 

 

 

相关文章:

STM32 实现 TCP 服务器与多个设备通信

目录 一、引言 二、硬件准备 三、软件准备 四、LWIP 协议栈的配置与初始化 五、创建 TCP 服务器 1.创建 TCP 控制块 2.绑定端口 3. 进入监听状态 4.设置接收回调函数 六、处理多个客户端连接 七、数据处理与通信管理 八、错误处理与资源管理 九、总结 一、引…...

EdgeNAT: 高效边缘检测的 Transformer

EdgeNAT: Transformer for Efficient Edge Detection 介绍了一种名为EdgeNAT的基于Transformer的边缘检测方法。 1. 背景与动机 EdgeNAT预测结果示例。(a, b):来自BSDS500的数据集的输入图像。(c, d):对应的真实标签。(e, f):由EdgeNAT检测到的边缘。(e)显示了由于颜色变化…...

Github优质项目推荐 - 第六期

文章目录 Github优质项目推荐 - 第六期一、【WiFiAnalyzer】,3.4k stars - WiFi 网络分析工具二、【penpot】,33k stars - UI 设计与原型制作平台三、【Inpaint-Anything】,6.4k stars - 修复图像、视频和3D 场景中的任何内容四、【Malware-P…...

力扣21~30题

21题(简单): 分析: 按要求照做就好了,这种链表基本操作适合用c写,python用起来真的很奇怪 python代码: # Definition for singly-linked list. # class ListNode: # def __init__(self, v…...

AGI|如何构建一个RAG应用?入门新手攻略!

目录 一、概述 二、过程概述 三、如何优化提问? 四、路由和高级查询 五、丰富索引结构 六、重排序上下文 七、总结 一、概述 Retrieval Augmented Generation RAG 检索增强的内容生成。 从字面上来看检索只是一种手段途径,在人工智能领域中存在多种…...

【.NET 8 实战--孢子记账--从单体到微服务】--角色(增加/删除/修改/查询)

本节我们将开始编写角色相关的接口 一、需求 本节的要做的需求如下: 编号需求标题需求内容1增加角色角色名称不能重复2删除角色角色逻辑删除3修改角色修改的名称不能和已有名称重复4查询角色不分页查询,根据角色名模糊匹配 二、Role类和Role表 这一…...

数据结构-栈与队列笔记

普通的双端队列 用栈实现队列 232. 用栈实现队列 - 力扣&#xff08;LeetCode&#xff09; import java.util.ArrayDeque; import java.util.Deque;class MyQueue {// 使用双端队列&#xff08;Deque&#xff09;来实现一个队列Deque<Integer> input; // 用于存放新加…...

DevExpress WPF中文教程:如何解决数据更新的常见问题?

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…...

SpringBoot基础(四):bean的多种加载方式

SpringBoot基础系列文章 SpringBoot基础(一)&#xff1a;快速入门 SpringBoot基础(二)&#xff1a;配置文件详解 SpringBoot基础(三)&#xff1a;Logback日志 SpringBoot基础(四)&#xff1a;bean的多种加载方式 目录 一、xml配置文件二、注解定义bean1、使用AnnotationCon…...

JavaScript网页设计案例:构建动态交互的在线图书管理系统

JavaScript网页设计案例&#xff1a;构建动态交互的在线图书管理系统 在当今的数字化时代&#xff0c;网页设计不仅仅是关于美观和布局&#xff0c;更重要的是用户体验和互动性。JavaScript&#xff0c;作为一种强大的编程语言&#xff0c;在网页开发中扮演着至关重要的角色&a…...

嵌入式数据结构中线性表的具体实现

大家好,今天主要给大家分享一下,如何使用数据结构中的线性表以及具体的实现。 第一:线性表的定义和表示方法 线性表的定义 – 线性表就是零个或多个相同数据元素的有限序列。 • 线性表的表示方法 – 线性表记为: L=(a0,∙∙∙∙∙∙∙∙ai-1aiai+1 ∙∙∙∙∙∙an-1) •…...

Redis高级篇 —— 分布式缓存

Redis高级篇 —— 分布式缓存 文章目录 Redis高级篇 —— 分布式缓存1 Redis持久化1.1 RDB1.2 RDB的fork原理1.3 RDB总结1.4 AOF持久化1.5 RDB和AOF的对比 2 Redis主从2.1 搭建主从架构2.2 数据同步原理2.2.1 全量同步2.2.2 增量同步 3 Redis哨兵3.1 哨兵的作用和原理3.1.1 哨兵…...

彩族相机内存卡恢复多种攻略:告别数据丢失

在数字时代&#xff0c;相机内存卡作为我们存储珍贵照片和视频的重要媒介&#xff0c;其数据安全性显得尤为重要。然而&#xff0c;意外删除、错误格式化、存储卡损坏等情况时有发生&#xff0c;导致数据丢失&#xff0c;给用户带来不小的困扰。本文将详细介绍彩族相机内存卡数…...

【C语言】计算需要的缓冲区大小

使用 snprintf 函数计算缓冲区大小的方法其实是一个常见的技巧,因为 snprintf 会返回所需的缓冲区大小,而不需要实际写入任何数据。当传入 NULL 指针时,`snprintf` 并不会尝试写入数据,而是仅仅返回格式化后的字符串长度。如果再加上终止符(即 \0),我们就可以知道实际需…...

Renesas R7FA8D1BH (Cortex®-M85) 上超声波测距模块(HC-SR04)驱动开发

目录 概述 1 软硬件 1.1 软硬件环境信息 1.2 开发板信息 1.3 调试器信息 2 硬件架构 2.1 硬件框架结构 2.2 测距模块&#xff08;HC-SR04&#xff09;介绍 2.2.1 HC-SR04特性 2.2.2 HC-SR04操作时序 2.2.3 计算距离 3 软件实现 3.1 FSP配置项目 3.1.1 配置IO口的外…...

短视频矩阵系统独立源码/源头开发

短视频矩阵系统独立源码/源头开发 #抖音矩阵系统源码开发 #短视频矩阵系统源码开发 #短视频seo源码开发 一、 抖音短视频seo矩阵系统源码开发&#xff0c;需要掌握以下技术&#xff1a; 网络编程&#xff1a;能够使用Python、Java或其他编程语言进行网络编程&#xff0c;比如…...

k8s部署jenkins集群,配置集群kubernetes plugin的pod模板

一、配置集群 填写k8s地址&#xff1a;https://kubernetes.default.svc.cluster.local 命名空间&#xff1a;kubernetes-plugin Jenkins地址&#xff1a;http://jenkins:18080 Jenkins通道&#xff1a;jenkins:50000 jenkins是容器别名 设置jenkinsslave的标签属性 二、…...

微软确认Word离奇Bug 命名不当会导致文件被删

微软近日确认Word应用中存在一个Bug&#xff0c;该漏洞可能导致用户在特定情况下错误地删除文件。该问题主要出现在文件命名过程中&#xff0c;如果用户在保存Word文件时采用特定的命名方式&#xff0c;文件可能会被移动到回收站。 根据微软支持中心的消息&#xff0c;如果用户…...

Vue包的安装使用

文章目录 vue介绍一、灵活易用1.渐进式框架2.简洁的语法 二、高效的响应式系统1.数据驱动2.响应式原理 三、强大的组件化开发1.组件化思想2.组件通信 四、丰富的生态系统1.插件和库2.社区支持 安装依赖删除新增文件夹components设置(1)home.vue(2)data.vue(3)zero.vue router配…...

大模型1-本地部署实现交互问答

任务 在本地部署大模型&#xff0c;调用大模型进行对话。 添加库&#xff1a; 1、Transformer Transformers 是由 Hugging Face 开发的一个开源库&#xff0c;广泛应用于自然语言处理&#xff08;NLP&#xff09;任务。其主要功能是简化了对大型预训练语言模型的加载和使用…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

Windows安装Miniconda

一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …...