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

2024.12.29(进程线程实现并发服务器)

作业

多进程多线程并发服务器实现一遍提交。

服务器

#include <myhead.h>
#define PORT 12345
#define IP "192.168.124.123"void *fun(void *fd)
{int newfd = *(int *)fd;char buff[1024];while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res == 0){printf("当前客户端已经退出\n");break;}printf("%s\n",buff);strcat(buff,"作业写完了");send(newfd,buff,sizeof(buff),0);}pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{//创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd == -1){perror("socket");return -1;}//绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//监听if(listen(oldfd,22)==-1){perror("listen");return -1;}struct sockaddr_in client;int client_len = sizeof(client);int newfd;pthread_t tid;while(1){//接收新客户端连入请求newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);if(newfd == -1){perror("accept");return -1;}//创建子线程与客户端通话if(pthread_create(&tid,NULL,fun,&newfd)==-1){perror("pthread_create");return -1;}}pthread_join(tid,NULL);close(newfd);close(oldfd);return 0;
}

客户端

#include <myhead.h>
#define PORT 12345
#define IP "192.168.124.123"
int main(int argc, const char *argv[])
{//创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd == -1){perror("socket");return -1;}//连接服务器struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("connect");return -1;}//收发消息char buff[1024];while(1){fgets(buff,sizeof(buff),stdin);buff[strlen(buff)-1] = '\0';send(oldfd,buff,strlen(buff),0);if(strcmp(buff,"quit")==0){break;}bzero(buff,sizeof(buff));recv(oldfd,buff,sizeof(buff),0);printf("服务器发来消息:%s\n",buff);}return 0;
}

学习笔记

端口号快速复用函数

        #include <sys/types.h> /* See NOTES */

        #include <sys/socket.h>

                int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);

        功能: 获取套接字或者其他层级的属性

        参数1:套接字描述符

        参数2:要获取的层级

        参数3:操作名称每一层级名称都不一样,具体见下表。

        参数4:变量的地址(表中的数据类型定义的变量)

        参数5:参数4 的大小。

        返回值:成功返回0,失败返回-1,并置位错误码

                int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

        功能:设置套接字或者其他层级的属性

        参数1:套接字描述符

        参数2:要获取的层级

        参数3:操作名称每一层级名称都不一样,具体见下表。

        参数4:变量的地址(表中的数据类型定义的变量)

        参数5:参数4 的大小。

        返回值:成功返回0,失败返回-1,并置位错误码

函数使用:

#include <myhead.h>int main(int argc, const char *argv[])
{int oldfd= socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){return -1;}//获取端口号快速复用的属性(默认关闭)int k;int k_len = sizeof(int);if(getsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&k,&k_len)==-1){perror("getsockopt");return -1;}printf("k = %d\n",k);//k=0//设置开启端口号快速复用属性(k!=0即可)k = 2;if(setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&k,sizeof(k))==-1){perror("setsockopt");return -1;}printf("开启端口号快速复用功能\n");return 0;
}

1、循环服务器模型

创建套接字

绑定

监听

循环:

        创建新的用于通讯的套接字

        收消息

        发消息

        关闭新的套接字

        关闭旧的套接字

代码:

缺点:新客户端要通信,必须使旧的客户端先退出。

#include <myhead.h>
#define IP "192.168.124.34"
#define PORT 6666
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定//3、监听//4、循环连接不同客户端//5、循环收发信息//1创建套接字    int oldfd = socket(AF_INET,SOCK_STREAM,0);if(-1==oldfd){perror("socket");return -1;}//2、绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//3、监听if(listen(oldfd,20)==-1){perror("listen");return -1;}//4、连接int newfd;struct sockaddr_in client;int client_len = sizeof(client);while(1){if((newfd = accept(oldfd,(struct sockaddr *)&client,&client_len))==-1){perror("accept");return -1;}//5、信息收发char buff[1024];while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res==0){printf("您的客户端已经下线\n");break;}printf("%s\n",buff);strcat(buff,"中午吃啥");send(newfd,buff,sizeof(buff),0);}}close(oldfd);close(newfd);return 0;
}

2、基于TCP并发服务器,目前有多进程和多线程并发。

2.1、多进程并发服务器,父进程只负责处理不同客户端的链接请求,每一个客户端请求连接就创建出一个子进程进行处理通话。

多进程服务器模型

1、子进程在哪创建

2、子进程怎么回收

3、终端打开的文件描述符有限。

1、多进程并发执行

模型:

定义信号处理函数,非阻塞回收僵尸进程。

绑定子进程退出时的信号。

1、创建套接字

2、绑定

3、监听

4、循环接收客户端信息

5、让父进程接收客户端请求并关闭新文件描述符,子进程关闭旧的描述符只负责数据收发。

服务器代码:

#include <myhead.h>
#define IP "192.168.124.34"
#define PORT 8888
void handle(int sss)
{if(sss==SIGCHLD){while(waitpid(-1,NULL,WNOHANG)>0);//非阻塞循环回收子进程资源}printf("回收成功\n");
}
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定//3、监听//4、父进程连接新的客户端//5、创建子父进程,父进程关闭新的描述符//5、子进程负责数据收发并关闭旧的描述符//1、创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(-1==oldfd){perror("socket");return -1;}//端口号快速复用int k = 999;if(setsockopt(oldfd,SOL_SOCKET,SO_REUSEADDR,&k,sizeof(k))==-1){perror("setsockopt");return -1;}printf("端口号快速复用成功\n");//2、绑定struct sockaddr_in server = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//3、监听if(listen(oldfd,88)==-1){perror("listen");return -1;}//4、父进程连接新的客户端 struct sockaddr_in client;int client_len = sizeof(client);while(1){int newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);if(newfd==-1){perror("accept");return -1;}printf("%s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));//输出新客户端信息//5、创建子父进程pid_t pid = fork();char buff[1024];if(pid>0){if(signal(SIGCHLD,handle)==SIG_ERR)//绑定子进程退出时的信号{perror("signal");return -1;}//父进程关闭新的描述符close(newfd);}else if(pid==0){//子进程关闭旧的描述符信息收发close(oldfd);while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res==0){printf("客户端已经退出\n");break;}printf("%s\n",buff);strcat(buff,"今天周日啊");send(newfd,buff,sizeof(buff),0);}exit(EXIT_SUCCESS);//子进程退出}else{perror("fork");return -1;}}close(oldfd);return 0;
}

TCP客户端:

#include <myhead.h>
#define IP "192.168.124.34"
#define SERPORT 8888
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定(不是必须绑定)//3、连接//4、收发消息int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){perror("socket");return -1;}
#if 0//绑定固定的IP和端口号(不是必须的)struct sockaddr_in client = {.sin_family  =AF_INET,.sin_port = htons(7899),//自定义端口号.sin_addr.s_addr = inet_addr("192.168.124.34")};if(bind(oldfd,(struct sockaddr *)&client,sizeof(client))==-1){perror("bind");return -1;}
#endif//连接服务器struct sockaddr_in server = {.sin_family  =AF_INET,.sin_port = htons(SERPORT),//注意端口号需要服务器端口.sin_addr.s_addr = inet_addr(IP)};if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("connect");return -1;}//收发消息char buff[1024];while(1){fgets(buff,sizeof(buff),stdin);buff[strlen(buff)-1] = '\0';send(oldfd,buff,strlen(buff),0);if(strcmp(buff,"quit")==0)//退出客户端{break;}bzero(buff,sizeof(buff));recv(oldfd,buff,sizeof(buff),0);//阻塞接收服务器消息printf("服务器发来消息:%s\n",buff);}return 0;
}

2、多线程并发服务器

1、大部分的多任务并发执行,我们都选择多线程,而不是多进程,因为多线程资源开销小,而且创建销毁比进程容易。

2、如果客户端过多,需要建立一个线程池,有客户端请求,就从线程池拿出一个线程分配给该客户端。

3、由于线程是提前创建好的,所以响应速度很快。

4、客户端退出后,线程会被销毁,由于线程占用资源较少,销毁也不会占用太多开销。

模型:

线程函数:

        收发消息

        关闭新描述符

        子线程退出

        建立原始套接字

        绑定主机IP 监听客户端

循环:

        accept:获取客户端请求

        建立子线程

        线程挂起

        关闭旧的文件描述符

多线程并发服务器

#include <myhead.h>
#define IP "192.168.124.34"
#define PORT 8888
void *fun(void *fd)
{int newfd = *(int *)fd; char buff[1024];while(1){int res = recv(newfd,buff,sizeof(buff),0);if(res==0){printf("客户端下线\n");break;}printf("%s\n",buff);strcat(buff,"5点放学");send(newfd,buff,sizeof(buff),0);}pthread_exit(NULL);//子线程退出
}int main(int argc, const char *argv[])
{//创建套接字int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){perror("socket");return -1;}//绑定struct sockaddr_in server = {.sin_family  =AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr  =inet_addr(IP)};if(bind(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("bind");return -1;}//监听if(listen(oldfd,20)==-1){perror("listen");return -1;}struct sockaddr_in client;int client_len = sizeof(client);int newfd;pthread_t tid;while(1){//接收新客户端连入请求newfd = accept(oldfd,(struct sockaddr *)&client,&client_len);if(newfd==-1){perror("accept");return -1;}//创建子线程与客户端通话if(pthread_create(&tid,NULL,fun,&newfd)==-1){perror("pthread_create");return -1;}}pthread_join(tid,NULL);//回收子线程资源close(newfd);close(oldfd);return 0;
}

多线程客户端:

#include <myhead.h>
#define IP "192.168.124.34"
#define SERPORT 8888
int main(int argc, const char *argv[])
{//1、创建套接字//2、绑定(不是必须绑定)//3、连接//4、收发消息int oldfd = socket(AF_INET,SOCK_STREAM,0);if(oldfd==-1){perror("socket");return -1;}
#if 0//绑定固定的IP和端口号(不是必须的)struct sockaddr_in client = {.sin_family  =AF_INET,.sin_port = htons(7899),//自定义端口号.sin_addr.s_addr = inet_addr("192.168.124.34")};if(bind(oldfd,(struct sockaddr *)&client,sizeof(client))==-1){perror("bind");return -1;}
#endif//连接服务器struct sockaddr_in server = {.sin_family  =AF_INET,.sin_port = htons(SERPORT),//注意端口号需要服务器端口.sin_addr.s_addr = inet_addr(IP)};if(connect(oldfd,(struct sockaddr *)&server,sizeof(server))==-1){perror("connect");return -1;}//收发消息char buff[1024];while(1){fgets(buff,sizeof(buff),stdin);buff[strlen(buff)-1] = '\0';send(oldfd,buff,strlen(buff),0);if(strcmp(buff,"quit")==0)//退出客户端{break;}bzero(buff,sizeof(buff));recv(oldfd,buff,sizeof(buff),0);//阻塞接收服务器消息printf("服务器发来消息:%s\n",buff);}return 0;
}作业:多进程多线程并发服务器实

思维导图

相关文章:

2024.12.29(进程线程实现并发服务器)

作业 多进程多线程并发服务器实现一遍提交。 服务器 #include <myhead.h> #define PORT 12345 #define IP "192.168.124.123"void *fun(void *fd) {int newfd *(int *)fd;char buff[1024];while(1){int res recv(newfd,buff,sizeof(buff),0);if(res 0){p…...

如何在 Ubuntu 上安装 PyTorch

简介 PyTorch 因其易用性、动态计算图和高效性而日益流行&#xff0c;成为实现深度学习模型的首选。如果你想探索这个工具并学习如何在 Ubuntu 上安装 PyTorch&#xff0c;本指南将对你有所帮助&#xff01; 在本教程中&#xff0c;我们将引导你完成在 Ubuntu 系统上使用 Pip…...

8-Gin 中间件 --[Gin 框架入门精讲与实战案例] 【文末有测试代码】

路由中间件 Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。它以性能好、中间件支持灵活著称&#xff0c;非常适合用来构建微服务或 RESTful API 服务。下面我将提供三个使用 Gin 的路由中间件的完整示例。 示例 1: 简单的日志记录中间件 这个中间件会在每个请求处理前后打…...

【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。

目录 一、toString() 方法是啥&#xff1f; &#xff08;一&#xff09;默认的 toString() 方法 &#xff08;二&#xff09;toString() 方法的作用 二、为啥要重写 toString() 方法&#xff1f; &#xff08;一&#xff09;提高代码的可读性 &#xff08;二&#xff09;…...

【论文笔记】Contrastive Learning for Sign Language Recognition and Translation

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: Contrastive Learning for…...

Gitlab17.7+Jenkins2.4.91实现Fastapi/Django项目持续发布版本详细操作(亲测可用)

一、gitlab设置&#xff1a; 1、进入gitlab选择主页在左侧菜单的下面点击管理员按钮。 2、选择左侧菜单的设置&#xff0c;选择网络&#xff0c;在右侧选择出站请求后选择允许来自webhooks和集成对本地网络的请求 3、webhook设置 进入你自己的项目选择左侧菜单的设置&#xff…...

一起来看--红黑树

【欢迎关注编码小哥&#xff0c;学习更多实用的编程方法和技巧】 红黑树是一种自平衡的二叉搜索树&#xff0c;广泛应用于计算机科学中&#xff0c;尤其是在实现关联数组和集合时。它的设计旨在确保在最坏情况下&#xff0c;基本动态集合操作&#xff08;如插入、删除和查找&am…...

SpringBoot整合篇 05、Springboot整合Redission

文章目录 前言Redission详细配置步骤pom依赖application.yaml配置类CacheConfigEnvironmentContext RedissionController单测 前言 本篇博客是SpringBoot整合Redission&#xff0c;若文章中出现相关问题&#xff0c;请指出&#xff01; 所有博客文件目录索引&#xff1a;博客…...

供应链系统设计-供应链中台系统设计(六)- 商品中心概念篇

概述 我们在供应链系统设计-中台系统设计系列&#xff08;五&#xff09;- 供应链中台实践概述 中描述了什么是供应链中台&#xff0c;供应链中台主要包含了那些组成部门。包括业务中台、通用中台等概念。为了后续方便大家对于中台有更深入的理解&#xff0c;我会逐一针对中台…...

胡闹厨房练习(三)

ScriptableObject 一、初步了解 1、实质:是一种特殊类型的Unity对象, 2、作用:用于存储大量数据,而不必依附于游戏场景中的某个GameObject。 3、特点: 可以在不增加场景中对象数量的情况下,管理和存储复杂的数据结构、配置信息、游戏状态等。 4、适用:非常适合用来…...

关于ESD(静电放电)等级的划分

关于ESD&#xff08;静电放电&#xff09;等级的划分&#xff0c;主要依据不同的测试模型和测试标准。以下是对HBM&#xff08;人体模型&#xff09;和CDM&#xff08;充电器件模型&#xff09;两种测试模型下ESD等级划分的详细解释&#xff1a; HBM ESD等级划分 HBM ESD等级…...

探究步进电机与输入脉冲的关系

深入了解步进电机 前言一、 步进电机原理二、 细分三、脉冲数总结 前言 主要是探究以下内容&#xff1a; 1、步进电机的步进角。 2、什么是细分。 3、脉冲的计算。 最后再扩展以下STM32定时器的计算方法。 一、 步进电机原理 其实语言描述怎么样都不直观&#xff0c;我更建议…...

基于YOLOV5+Flask安全帽RTSP视频流实时目标检测

1、背景 在现代工业和建筑行业中&#xff0c;安全始终是首要考虑的因素之一。特别是在施工现场&#xff0c;工人佩戴安全帽是确保人身安全的基本要求。然而&#xff0c;人工监督难免会有疏漏&#xff0c;尤其是在大型工地或复杂环境中&#xff0c;确保每个人都佩戴安全帽变得非…...

Windows内置的服务器IIS(Internet Information Services)托管网站

一. 安装IIS 打开控制面板&#xff1a;在开始菜单搜索“控制面板”并打开它。程序和功能&#xff1a;点击“程序”然后选择“程序和功能”。启用或关闭Windows功能&#xff1a;在左侧菜单中选择“启用或关闭Windows功能”。查找并勾选IIS&#xff1a;在弹出的窗口中&#xff0c…...

虚幻引擎结构之UObject

一. UObject 的介绍 UObject 是虚幻引擎中的核心基础类,所有其他游戏对象和资源类都直接或间接地继承自它。作为虚幻引擎的基石,UObject 提供了多项关键功能,包括内存管理、序列化、反射(introspection)、垃圾回收以及元数据支持。在虚幻引擎中,UObject 类的实例通常被称…...

js的Reflect对象

Reflect 对象是 JavaScript ES6 中引入的一个内建对象&#xff0c;它提供了一系列与对象操作相关的方法。这些方法与 Object 对象上的方法类似&#xff0c;但在行为上有一些差异&#xff0c;并且更加规范和统一。Reflect 对象并不是一个构造函数&#xff0c;不能被 new 操作符调…...

this指向了谁?

看函数在执行的时候是如何调用的&#xff0c; 1 如果这个函数是用普通函数调用模式来进行调用&#xff0c;它内部的this指向了window; 2 如果一个函数在调用的时候是通过对象方法模式来进行调用&#xff0c;则它内部的this就是我们的对象; 3 如果一个函数在调用的时候通过构…...

基于Resnet、LSTM、Shufflenet及CNN网络的Daily_and_Sports_Activities数据集仿真

在深度学习领域&#xff0c;不同的网络结构设计用于解决特定的问题。本文将详细分析四种主流网络结构&#xff1a;卷积神经网络&#xff08;CNN&#xff09;、残差网络&#xff08;ResNet&#xff09;、长短期记忆网络&#xff08;LSTM&#xff09;和洗牌网络&#xff08;Shuff…...

mac系统vsCode中使用Better Comments在.vue文件里失效

问题&#xff1a;关于Better Comments默认在html、TS、JS中有效&#xff0c;在vue中无效,需要单独进行配置 windows系统可以参考友链Better Comments&#xff08;注释高亮&#xff09;在vue文件里失效的问题 关于Better Comments电脑的配置路径&#xff1a; Windows系统&…...

UE5.3 C++ Ceiusm中的POI 制作3DUI 结合坐标转化

一.核心思路WidgetComponent CesiumGloberAnchor 二.先制作POI 创建C Actor来制作&#xff0c;APOI。直接上代码 #pragma once#include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "CesiumGlobeAnchorComponent.h" #includ…...

Zotero插件市场:变革学术研究工具管理的创新解决方案

Zotero插件市场&#xff1a;变革学术研究工具管理的创新解决方案 【免费下载链接】zotero-addons Zotero add-on to list and install add-ons in Zotero 项目地址: https://gitcode.com/gh_mirrors/zo/zotero-addons 在数字化学术研究的进程中&#xff0c;文献管理工具…...

别再只盯着准确率了!手把手教你用Python实现NDCG和MAP,搞定搜索推荐系统评估

别再只盯着准确率了&#xff01;手把手教你用Python实现NDCG和MAP&#xff0c;搞定搜索推荐系统评估 当你在优化推荐算法时&#xff0c;是否曾为选择评估指标而纠结&#xff1f;准确率、召回率这些传统指标虽然直观&#xff0c;却无法捕捉排序质量这一关键维度。本文将带你深入…...

15天深度体验:micro编辑器状态栏系统监控完全指南

15天深度体验&#xff1a;micro编辑器状态栏系统监控完全指南 【免费下载链接】micro A modern and intuitive terminal-based text editor 项目地址: https://gitcode.com/gh_mirrors/mi/micro micro编辑器是一款现代化的终端文本编辑器&#xff0c;以其直观易用和高度…...

OpenClaw成本优化方案:自建Qwen3-VL:30B替代高价多模态API

OpenClaw成本优化方案&#xff1a;自建Qwen3-VL:30B替代高价多模态API 1. 为什么需要关注OpenClaw的成本问题 第一次用OpenClaw完成多模态任务时&#xff0c;我被账单吓了一跳。当时需要处理200张产品图片的分类和描述生成&#xff0c;调用某商业多模态API后&#xff0c;费用…...

AutoDL云服务器避坑指南:从PyTorch到Jupyter,手把手搞定GPU环境配置

AutoDL云服务器GPU环境配置实战&#xff1a;从镜像选择到Jupyter避坑全攻略 第一次在AutoDL这类云GPU平台上配置深度学习环境时&#xff0c;那种既兴奋又忐忑的心情我至今记忆犹新。看着琳琅满目的镜像选项和复杂的版本匹配要求&#xff0c;稍有不慎就会陷入"版本地狱&qu…...

别再只盯着GPU了!聊聊华为昇腾310/910芯片在AI推理和训练中的实战选型心得

华为昇腾芯片实战选型指南&#xff1a;如何用310/910构建高性价比AI计算方案 当你在深夜调试一个即将上线的图像识别模型时&#xff0c;服务器机房的轰鸣声和不断攀升的电费账单可能比代码bug更让人焦虑。三年前&#xff0c;我们团队就面临这样的困境——用8块NVIDIA V100训练的…...

量子行走:从理论到Python实现——量子力学原理与Qubit物理

目录 2. 量子力学原理与Qubit物理 2.1 量子比特的物理实现 2.1.1 双能级系统建模 2.1.2 布洛赫球表示与可视化 2.2 叠加与纠缠现象 2.2.1 量子叠加原理 2.2.2 量子纠缠理论 2.3 量子测量与退相干 2.3.1 测量公设的实现 2.3.2 噪声与退相干机制 2. 量子力学原理与Qubi…...

嵌入式正交编码器软件解码库设计与实现

1. QuadratureEncoder 库概述QuadratureEncoder 是一个专为嵌入式系统设计的正交编码器信号处理库&#xff0c;面向 STM32、ESP32、nRF52 等主流 MCU 平台&#xff0c;提供高精度、低开销、抗干扰的旋转位置与速度检测能力。该库不依赖特定硬件外设&#xff08;如 STM32 的 TIM…...

用LVGL玩转嵌入式UI:5个实战控件代码详解(按钮/滑块/图片/标签/开关)

LVGL嵌入式UI开发实战&#xff1a;五大核心控件深度解析与代码优化 在资源受限的嵌入式设备上实现流畅美观的用户界面&#xff0c;一直是开发者面临的挑战。LVGL&#xff08;Light and Versatile Graphics Library&#xff09;作为一款轻量级开源图形库&#xff0c;凭借其丰富的…...

STM32串口通信原理与实现详解

串口通信技术深度解析&#xff1a;从原理到STM32实现1. 串口通信基础概念1.1 数据传送方向分类串行通信根据数据传输方向可分为三种基本模式&#xff1a;单工模式&#xff1a;数据仅支持单向传输&#xff0c;如传统的广播系统。发送端和接收端角色固定&#xff0c;硬件上只需单…...