STM32 实现 TCP 服务器与多个设备通信
目录
一、引言
二、硬件准备
三、软件准备
四、LWIP 协议栈的配置与初始化
五、创建 TCP 服务器
1.创建 TCP 控制块
2.绑定端口
3. 进入监听状态
4.设置接收回调函数
六、处理多个客户端连接
七、数据处理与通信管理
八、错误处理与资源管理
九、总结
一、引言
在嵌入式系统开发中,常常需要实现设备之间的网络通信。STM32 作为一款广泛应用的微控制器,结合网络通信功能可以实现与多个设备的交互。本文将介绍如何在 STM32 上实现 TCP 服务器端,以便与多个设备进行通讯。
二、硬件准备
- STM32 开发板:选择一款带有以太网接口的 STM32 开发板,如 STM32F429 系列等。
- 以太网模块(可选):如果开发板没有内置以太网接口,可以选择一个外接的以太网模块,如 W5500 等。
- 网络连接:将开发板连接到同一局域网内,确保其他设备可以通过网络访问到 STM32 开发板。
三、软件准备
- 开发环境:如 Keil MDK、IAR Embedded Workbench 等。
- LWIP(Lightweight IP)协议栈:LWIP 是一个轻量级的 TCP/IP 协议栈,适用于嵌入式系统。可以从 LWIP 官方网站下载并集成到开发环境中。
- STM32 库:使用 STM32 的官方库或者其他第三方库来进行硬件驱动和开发。
四、LWIP 协议栈的配置与初始化
- 将 LWIP 协议栈的源文件添加到 STM32 项目中,并设置正确的编译选项。
- 在项目的初始化代码中,调用 LWIP 的初始化函数
lwip_init()
,完成协议栈的初始化。 - 根据实际需求,配置 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(¤t->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. 用栈实现队列 - 力扣(LeetCode) import java.util.ArrayDeque; import java.util.Deque;class MyQueue {// 使用双端队列(Deque)来实现一个队列Deque<Integer> input; // 用于存放新加…...

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

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

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

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

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

Renesas R7FA8D1BH (Cortex®-M85) 上超声波测距模块(HC-SR04)驱动开发
目录 概述 1 软硬件 1.1 软硬件环境信息 1.2 开发板信息 1.3 调试器信息 2 硬件架构 2.1 硬件框架结构 2.2 测距模块(HC-SR04)介绍 2.2.1 HC-SR04特性 2.2.2 HC-SR04操作时序 2.2.3 计算距离 3 软件实现 3.1 FSP配置项目 3.1.1 配置IO口的外…...

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

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

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

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

大模型1-本地部署实现交互问答
任务 在本地部署大模型,调用大模型进行对话。 添加库: 1、Transformer Transformers 是由 Hugging Face 开发的一个开源库,广泛应用于自然语言处理(NLP)任务。其主要功能是简化了对大型预训练语言模型的加载和使用…...

鸿蒙架构-系统架构师(七十八)
1信息加密是保证系统机密性的常用手段。使用哈希校验是保证数据完整性的常用方法。可用性保证合法用户对资源的正常访问,不会被不正当的拒绝。()就是破坏系统的可用性。 A 跨站脚本攻击XSS B 拒绝服务攻击DoS C 跨站请求伪造攻击CSRF D 缓…...

大数据存储计算平台EasyMR:多集群统一管理助力企业高效运维
随着全球企业进入数字化转型的快车道,数据已成为企业运营、决策和增长的核心驱动力。为了处理海量数据,同时应对数据处理的复杂性和确保系统的高可用性,企业往往选择部署多个Hadoop集群,这样的策略可以将生产环境、测试环境和灾备…...

代理IP的类型及其在爬虫中的应用
1 动态住宅代理 这些IP地址来自真实的住宅用户,因此具有很高的匿名性和隐私性,不易被别为代理IP。而增加了爬虫任务的安全性。这类代理有以下特点: 高安全性:使用这类代理可发起真实有效的请求,提高爬虫效率的同时&am…...

鸿蒙Swiper动态加载翻页数据(等同于安卓动态加载viewPager)
我这里是加载一个实体类列表 类似 List 的数据,那么首先写一个dataSource: export class MyDataSource implements IDataSource {private list: MyBean[] []constructor(list: MyBean[]) {this.list list}totalCount(): number {return this.list.len…...

嵌入式面试——FreeRTOS篇(八) Tickless低功耗
本篇为:FreeRTOS Tickless 低功耗模式篇 一、低功耗模式简介 1、低功耗介绍 答: 很多应用场合对于功耗的要求很严格,比如可穿戴低功耗产品、物联网低功耗产品等;一般MCU都有相应的低功耗模式,裸机开发时可以使用MCU的…...

基于facefusion的换脸
FaceFusion是一个引人注目的开源项目,它专注于利用深度学习技术实现视频或图片中的面部替换。作为下一代换脸器和增强器,FaceFusion在人脸识别和合成技术方面取得了革命性的突破,为用户提供了前所未有的视觉体验。 安装 安装基础软件 安装…...

Hive数仓操作(十三)
一、JSON 数据 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,在不同的编程语言之间进行数据传输时非常通用和常用。JSON 格式简单直观,易于阅读和编写,并且可以被大多数编程语言轻松解析和生成。 1.…...

MyBatis XML映射文件
XML映射文件 XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)XML映射文件的namespace属性为Mapper接口全限定名一致XML映射文件中SQL语句的id与Mapper接口中的方法名一致,并保持返…...

「PYTHON」配置支持cuda计算的torch环境
本教程用于配置可支持cuda加速计算的torch环境 如果单纯使用命令行的pip安装torch,几乎都是cpu版本的,所以想要下载支持cuda的torch,我们只能通过手动下载安装包到本地,再使用pip从下载好的本地文件离线安装 而要想使用cuda加速…...

Chromium 中chrome.history扩展接口c++实现
一、前端 chrome.history定义 使用 chrome.history API 与浏览器的已访问网页的记录进行交互。您可以在浏览器的历史记录中添加、移除和查询网址。如需使用您自己的版本替换历史记录页面,请参阅覆盖网页。 更多参考:chrome.history | API | Chrome…...