嵌入式养成计划-31-网络编程----TCP的并发服务器模型------IO模型--IO多路复用

六十七、 TCP的并发服务器模型

67.1 循环服务器模型

  • 一次只能处理一个客户端,当上一个客户端退出后,才能处理下一个客户端
  • 缺点:无法同时处理多个客户端

代码模型

sfd = socket();
bind();
listen();
while(1){newfd = accept();while(1){recv();send();    }close(newfd);
}
close(sfd);

67.2 并发服务器模型

  • 目的:可以同时处理多个客户端的请求。
  • 实现:创建多进程或者创建多线程实现
    • 父进程 / 主线程 只负责连接(accept)
    • 子进程 / 分支线程只负责与客户端交互(recv / send);

67.2.1 多进程并发服务器

67.2.1.1 代码模型

void handler(int sig){while(waitpid(-1, NULL, WNOHANG) > 0);
}signal(17, handler);
sfd = socket();
bind();
listen();
while(1){newfd = accept();cpid = fork();if(0 == cpid){close(sfd);while(1){recv();send();        }close(newfd);exit(0);         //退出子进程    }close(newfd);
}
close(sfd);

67.2.1.2 代码示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>#define ERR_MSG(msg) do{\fprintf(stderr, "__%d__ ", __LINE__);\perror(msg);\
}while(0)#define PORT 8888               //端口号的网络字节序,1024~49151
#define IP "192.168.125.55"     //本机IP,ifconfigint deal_cli_msg(int newfd, struct sockaddr_in cin);void handler(int sig)
{//循环回收僵尸进程//有子进程,没有僵尸进程  == 0//没有子进程,也没有僵尸进程 ==-1、while(waitpid(-1, NULL, WNOHANG) > 0); return ;
}int main(int argc, const char *argv[])
{//捕获17号 SIGCHLD信号if(signal(SIGCHLD, handler) == SIG_ERR){   ERR_MSG("signal");return -1; }   //创建流式套接字int sfd = socket(AF_INET, SOCK_STREAM, 0); if(sfd < 0){   ERR_MSG("socket");return -1; }   printf("socket create success  sfd=%d\n", sfd);//允许端口快速复用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){   ERR_MSG("setsockopt");return -1; }   printf("允许端口快速复用成功\n");//填充地址信息结构体给bind函数绑定使用;//真实的地址信息结构体根据地址族指定,AF_INET:man 7 ipstruct sockaddr_in sin;sin.sin_family      = AF_INET;          //必须填AF_INET;sin.sin_port        = htons(PORT);      //端口号的网络字节序,1024~49151sin.sin_addr.s_addr = inet_addr(IP);    //本机IP的网络字节序,ifconfig//绑定服务器的地址信息---》必须绑定if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0){   ERR_MSG("bind");return -1; }   printf("bind success\n");//将套接字转换成被动监听状态if(listen(sfd, 128) < 0){   ERR_MSG("listen");                                                                                    return -1; }   printf("listen success\n");struct sockaddr_in cin;     //存储客户端的地址信息socklen_t addrlen = sizeof(cin);int newfd = -1; pid_t cpid = -1; while(1){   //父进程只负责连接newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){ERR_MSG("accept");return -1; }printf("[%s:%d] newfd=%d 客户端连接成功__%d__\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);//能运行到当前位置,则代表有客户端连接成功,//此时需要创建一个子进程,专门用于与客户端交互cpid = fork();if(0 == cpid){close(sfd);     //子进程只负责交互,sfd没有用deal_cli_msg(newfd, cin);close(newfd);exit(0);        //退出子进程,子进程只负责交互,不允许回到accept函数。}else if(cpid < 0){ERR_MSG("fork");;return -1; }close(newfd);       //在父进程中newfd没有用}   //关闭套接字close(sfd);return 0;
}//子进程负责与客户端交互的函数
int deal_cli_msg(int newfd, struct sockaddr_in cin)
{char buf[128] = ""; ssize_t res = 0;while(1){   bzero(buf, sizeof(buf));//接收数据res = recv(newfd, buf, sizeof(buf), 0); if(res < 0){ERR_MSG("recv");return -1; }else if(0 == res){printf("[%s:%d] newfd=%d 客户端下线__%d__\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);break;}printf("[%s:%d] newfd=%d : %s __%d__\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, buf, __LINE__);if(strcmp(buf, "quit") == 0)break;//发送数据strcat(buf, "*_*");     //数据可以选择冲终端获取if(send(newfd, buf, sizeof(buf), 0) < 0){ERR_MSG("send");return -1; }printf("send success\n");}   return 0;
}

67.2.2 多进程并发服务器

67.2.2.1 代码模型

sfd = socket();
bind();
listen();
while(1){newfd = accept();pthread_create( , , deal_cli_msg, );pthread_detach(tid);
}
close(sfd);void* deal_cli_msg(void* arg){while(1){recv();send();    }close(newfd);pthread_exit();
}

67.2.2.1 代码示例

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>#define ERR_MSG(msg) do{\fprintf(stderr, "__%d__ ", __LINE__);\perror(msg);\
}while(0)#define PORT 8888               //端口号的网络字节序,1024~49151
#define IP "192.168.125.55"     //本机IP,ifconfig//传递给线程执行体的数据封装成结构体
struct Climsg
{int newfd;struct sockaddr_in cin;
};void* deal_cli_msg(void* arg) ;     //void* arg = &infoint main(int argc, const char *argv[])
{//创建流式套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if(sfd < 0){ERR_MSG("socket");return -1;}printf("socket create success  sfd=%d\n", sfd);//允许端口快速复用int reuse = 1;if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){ERR_MSG("setsockopt");return -1;}printf("允许端口快速复用成功\n");//填充地址信息结构体给bind函数绑定使用;//真实的地址信息结构体根据地址族指定,AF_INET:man 7 ipstruct sockaddr_in sin;sin.sin_family      = AF_INET;          //必须填AF_INET;sin.sin_port        = htons(PORT);      //端口号的网络字节序,1024~49151sin.sin_addr.s_addr = inet_addr(IP);    //本机IP的网络字节序,ifconfig//绑定服务器的地址信息---》必须绑定if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("bind");return -1;}printf("bind success\n");//将套接字转换成被动监听状态if(listen(sfd, 128) < 0){ERR_MSG("listen");return -1;}printf("listen success\n");struct sockaddr_in cin;     //存储客户端的地址信息socklen_t addrlen = sizeof(cin);int newfd = -1;pthread_t tid;      //存储线程tid号struct Climsg info;while(1){//代码先阻塞在accept函数,再断开连接关闭的文件描述符时//accept函数每次在阻塞的时候,会先预选一个没有被占用的文件描述符//当解除阻塞的时候,若预选的文件描述符没有被占用,则直接返回预选的文件描述符//若预选的文件描述符被占用,则会重新遍历一个没有被使用的文件名描述符返回//主线程只负责连接(accept)newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if(newfd < 0){ERR_MSG("accept");return -1;}printf("[%s:%d] newfd=%d 客户端连接成功__%d__\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);info.newfd = newfd;info.cin = cin;//能运行到当前位置,则代表有客户端连接成功,//此时需要创建一个分支线程,专门用于处理客户端的交互if(pthread_create(&tid, NULL, deal_cli_msg, (void*)&info) != 0){fprintf(stderr, "pthread_create failed __%d__\n", __LINE__);return -1;}pthread_detach(tid);    //分离线程}//关闭套接字close(sfd);return 0;
}//线程执行体 ---> 分支线程只负责交互
void* deal_cli_msg(void* arg)       //void* arg = &info
{//newfd和cin必须另存,每个客户端都有自己独立的通信文件描述符和地址信息。//如果使用全局变量,或者指针方式间接访问,会导致所有线程共用一份newfd和cin,//那么newfd和cin会被覆盖int newfd = ((struct Climsg*)arg)->newfd;struct sockaddr_in cin = ((struct Climsg*)arg)->cin;char buf[128] = "";ssize_t res = 0;while(1){bzero(buf, sizeof(buf));//接收数据res = recv(newfd, buf, sizeof(buf), 0);if(res < 0){ERR_MSG("recv");break;}else if(0 == res){printf("[%s:%d] newfd=%d 客户端下线__%d__\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);break;}printf("[%s:%d] newfd=%d : %s __%d__\n", \inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, buf, __LINE__);if(strcmp(buf, "quit") == 0)break;//发送数据strcat(buf, "*_*");     //数据可以选择冲终端获取if(send(newfd, buf, sizeof(buf), 0) < 0){ERR_MSG("send");break;}printf("send success\n");}close(newfd);pthread_exit(NULL);
}

六十八、IO模型

68.1 阻塞IO

  1. 最常用,最简单,效率最低的。
  2. 创建套接字文件描述符后,默认处于阻塞IO模式;
  3. read, write, recv, send, recvfrom ,sendto,accept

68.2 非阻塞IO

  1. .防止进程阻塞在IO函数上,当一个程序使用了非阻塞IO模式的套接字,那么它需要使用一个循环来不停的判断该文件描述符是否有数据可读,称之为polling;
  2. 应用程序不停的polling内核监测IO事件是否产生,cpu消耗率高;
  3. IO中导致函数阻塞的原因是因为文件描述符有阻塞属性。
read阻塞:文件描述符有阻塞属性:0号文件描述符有阻塞属性 + 读属性---0号文件描述符加上非阻塞属性
  • 修改 IO 为非阻塞方式
1. 先获取0号文件描述符原有属性
2. 在原有属性的基础上将阻塞  设置为  非阻塞
3. 将修改后的属性重新设置回0号文件描述符中

fcntl函数

功能:获取/设置文件描述属性;
原型:#include <unistd.h>#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */ );
参数:int fd:指定要设置或者获取属性的文件描述符int cmd:F_GETFL (void):获取属性,第三个参数不用填,获取到的属性在返回值返回;F_SETFL (int):设置属性,第三个参数是int类型;

68.3 信号驱动IO

  1. 异步通信方式;
  2. 信号驱动IO是指预先告诉内核,使得某个文件描述符发生IO事件的时候,内核会通知相关进程
    SIGIO;
  3. 对于TCP而言,信号驱动IO对TCP没有用。因为信号产生过于频繁,而且不能区分是哪个文件描述符发生的。

68.4 IO多路复用(重点!!!)

  1. 进程中如果同时需要处理多路输入输出流,
  2. 在无法用多进程多线程,可以选择用IO多路复用;
  3. 由于不需要创建新的进程和线程,减少系统的资源开销,减少上下文切换的次数。
    1. 上下文:运行一个进程所需要的所有资源
    2. 上下文切换:从A进程切换到B进程,A进程的资源要完全替换成B进程的,是一个耗时操作。
  4. 增加并发量的时候可以使用IO多路复用
  5. 允许同时对多个IO进行操作,内核一旦发现进程执行一个或多个IO事件,会通知该进程。
    在这里插入图片描述

68.4.1 select

68.4.1.1 select函数

功能:阻塞函数,让内核监测集合中是否有文件描述符准备就绪,若准备就绪则解除阻塞;当函数解除阻塞后,集合中会只剩下产生事件的文件描述符;例如:0号准备就绪,则集合中只剩下0号sfd准备就绪,则集合中只能下sfd;0和sfd均准备就绪,则0和sfd均存在若不将数据从触发事件的文件描述符对应的空间中取出,此时该文件描述符一直处于就绪状态。
原型:#include <sys/select.h>#include <sys/time.h>#include <sys/types.h>#include <unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
参数:int nfds:需要填充三个集合中最大的文件描述符编号+1;fd_set *readfds, fd_set *writefds,fd_set *exceptfds:读集合,写,其他集合,若集合不使用,填NULL; 一般只用读集合;struct timeval *timeout:设置超时时间;   1. 若不想设置超时时间,填NULL,则当前函数会一直阻塞,直到集合中有文件描述符准备就绪。2. 设置超时时间; 若时间到后依然没有事件产生,则该函数解除阻塞,且返回失败情况。struct timeval {long    tv_sec;         /* seconds */long    tv_usec;        /* microseconds */};
返回值:>0, 成功返回成功触发事件的文件描述符个数;=0, 超时了;失败,返回-1,更新errno;操作集合的函数   void FD_CLR(int fd, fd_set *set);    //将fd从集合中删除int  FD_ISSET(int fd, fd_set *set);  //判断fd是否在集合中void FD_SET(int fd, fd_set *set);    //将fd添加到结合中void FD_ZERO(fd_set *set);           //清空集合

68.4.1.2 select的TCP模型

sfd = socket();
bind();
listen();
while(1){tempfds = readfds;select(maxfd+1, &tempfds, NULL, NULL, NULL);for(int i=0; i<=maxfd; i++){if(FD_ISSET(i, &tempfds) == 0) continue;if(0 == i){fgets();        }else if(sfd == i){newfd = accept()FD_SET(newfd, &readfds);maxfd = maxfd>newfd?maxfd:newfd;        }    else{res = recv(i, );if(0 == res){close(i);    FD_CLR(i, &readfds);while(!FD_ISSET(maxfd, &readfds) && maxfd-->=0);            }send();                 }}
}
close(sfd);

68.4.1.3 select的TCP服务器代码

68.4.1.4 select的TCP客户端代码

这俩的代码都在这个链接里面,再放进来就太长了,写文档都在卡

IO多路复用实现TCP客户端与TCP并发服务器

68.4.2 poll

68.4.2.1 poll函数

功能:阻塞函数,阻塞等待集合中有文件描述符准备就绪,若准备就绪,则立即解除阻塞。
原型:#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数:struct pollfd *fds:指定要监测的集合struct pollfd {int   fd;         /* file descriptor */     指定要监测的文件描述符short events;     /* requested events */    指定要监测的事件short revents;    /* returned events */     实际产生的事件};事件:POLLIN     这里有数据可读POLLOUT    可写POLLERR    错误事件,只有在revents中有效nfds_t nfds:指定要监测的文件描述符的个数;int timeout:超时时间,以ms为单位>0, 设置超时时间,以ms为单位;=0, 不阻塞,即使没有文件描述符准备就绪,该函数不阻塞;<0, 不设置超时时间,一直阻塞,直到当集合中有文件描述符准备就绪,该函数解除阻塞;
返回值:>0, 实际产生事件的文件描述符个数;=0, 超时了=-1,更新errno;

68.4.2.2 poll的TCP客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>#define ERR_MSG(msg) do{\fprintf(stderr, "__%d__ ", __LINE__);\perror(msg);\
}while(0)#define SER_PORT 8888           //服务器绑定的端口号
#define SER_IP "192.168.125.55"     //服务器绑定的IP                                                  int main(int argc, const char *argv[])
{//创建流式套接字int cfd = socket(AF_INET, SOCK_STREAM, 0);if(cfd < 0){ERR_MSG("socket");return -1;}printf("socket create success  cfd=%d\n", cfd);//允许端口快速复用int reuse = 1;if(setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0){ERR_MSG("setsockopt");return -1;}printf("允许端口快速复用成功\n");//绑定客户端的地址信息---》非必须绑定//客户端若不绑定,则操作系统会自动给客户端绑定本机IP及随机端口//填充服务器的地址信息结构体给connect函数使用;//真实的地址信息结构体根据地址族指定,AF_INET:man 7 ipstruct sockaddr_in sin;sin.sin_family      = AF_INET;              //必须填AF_INET;sin.sin_port        = htons(SER_PORT);      //服务器绑定的端口号sin.sin_addr.s_addr = inet_addr(SER_IP);    //服务器绑定的IP//连接服务器,想要连接哪个服务器就需要填充哪个服务器绑定的地址信息if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) < 0){ERR_MSG("connect");return -1;}printf("connect server success\n");//创建集合struct pollfd fds[2] = {0};//将需要的文件描述符添加到集合中fds[0].fd       = 0;        //指定要监测0号文件描述符fds[0].events   = POLLIN;   //指定要监测读事件fds[1].fd       = cfd;      //指定要监测cfdfds[1].events   = POLLIN;int p_res = 0;char buf[128] = "";ssize_t res = 0;while(1){p_res = poll(fds, sizeof(fds)/sizeof(struct pollfd), -1);if(p_res < 0){ERR_MSG("poll");return -1;}else if(0 == p_res){printf("time out");break;}//能运行到当前位置,则代表集合中有文件描述符准备就绪//即revents成员中有数据了,//判断revents中是否有POLLIN事件if(fds[0].revents & POLLIN){printf("触发键盘输入事件\n");bzero(buf, sizeof(buf));fgets(buf, sizeof(buf), stdin);buf[strlen(buf)-1] = '\0';//发送数据if(send(cfd, buf, sizeof(buf), 0) < 0)//if(write(cfd, buf, sizeof(buf)) < 0){ERR_MSG("send");return -1;}printf("send success\n");}if(fds[1].revents & POLLIN){bzero(buf, sizeof(buf));//接收数据res = recv(cfd, buf, sizeof(buf), 0);//res = read(cfd, buf, sizeof(buf));if(res < 0){ERR_MSG("recv");return -1;}else if(0 == res){printf("[%s:%d] cfd=%d 服务器下线__%d__\n", \SER_IP, SER_PORT, cfd, __LINE__);break;}printf("[%s:%d] cfd=%d :%s __%d__\n", \SER_IP, SER_PORT, cfd, buf, __LINE__);}}//关闭套接字close(cfd);return 0;
}

68.4.2.3 epoll(到驱动部分再讲解)(是重点)

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

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

相关文章

vue-3

一、文章内容概括 1.生命周期 生命周期介绍生命周期的四个阶段生命周期钩子声明周期案例 2.工程化开发入门 工程化开发和脚手架项目运行流程组件化组件注册 二、Vue生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09…

取消激光雷达/不升级Orin,小鹏P5改款背后的行业「痛点」

9月25日&#xff0c;小鹏汽车正式发布了旗下改款车型—2024款小鹏P5&#xff08;15.69-17.49万元&#xff09;。车型精简、降本增效&#xff08;减配&#xff09;成为新亮点。 在配置变化方面&#xff0c;智驾成为牺牲品。其中&#xff0c;高配Pro车型继续保留英伟达Xavier&…

如何领取京东优惠券用微信支付还是用支付宝支付购买商品?

京东用微信支付还是用支付宝支付&#xff1f; 京东商城购物不支持支付宝支付&#xff0c;现京东商城支持的支付方式包括&#xff1a;京东支付、银行卡、信用卡、微信支付、云闪付等&#xff1b; 京东如何领取优惠券用微信支付购物&#xff1f; 1、打开京东APP&#xff0c;挑选…

存档&改造【04】二维码操作入口设置细节自动刷新设置后的交互式网格

因为数据库中没有数据无法查看设置效果&#xff0c;于是自己创建了个测试数据表&#xff0c;用来给demo测试 -- 二维码操作入口设置 create table JM_QR_CODE(QR_CODE_ID NUMBER generated as identity primary key,SYSTEM_ID NUMBER(20) not null,IS_ENAB…

安装torchtext遇到的坑及解决办法

刚开始秉着需要什么就pip install什么的原则直接pip install torchtext&#xff0c;结果&#xff1a; 把我这个环境打乱了&#xff0c;自作主张的删掉之前的很多包重新安装了其他版本的包而不是自适应的安装当前torch所对应的torchtext。因为这个环境比较重要也用在其他的工程…

【FreeRTOS】【STM32】01从零开始的freertos之旅 浏览源码下的文件夹

基于野火以及正点原子 在打开正点原子的资料pdf时&#xff0c;我遇到了pdf无法复制粘贴的问题&#xff0c;这里有个pdf解锁文字复制功能的网址&#xff0c;mark一下。超级pdf 参考资料《STM32F429FreeRTOS开发手册_V1.2》 官方资料 FreeRTOS 的源码和相应的官方书籍均可从官…

机器人中的数值优化(二十一)—— 伴随灵敏度分析、线性方程组求解器的分类和特点、优化软件

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

模型训练环境相关(CUDA、PyTorch)

模型训练环境相关&#xff08;CUDA、PyTorch&#xff09; 1. 查看当前 GPU 所能支持的最高版本的 CUDA2. 如何判断是否安装了 CUDA3. 安装 PyTorch3.1 创建虚拟环境3.2 激活并进入虚拟环境3.3 安装 PyTorch 1. 查看当前 GPU 所能支持的最高版本的 CUDA 打开 NVIDIA 控制面板&a…

QT基础入门——文件操作(六)

前言&#xff1a; 文件操作是应用程序必不可少的部分。Qt 作为一个通用开发库&#xff0c;提供了跨平台的文件操作能力。Qt 通过QIODevice提供了对 I/O 设备的抽象&#xff0c;这些设备具有读写字节块的能力。 目录 一、QFile文件读写操作 1.QFile file( path 文件路径) 2…

Flink状态管理与检查点机制

1.状态分类 相对于其他流计算框架,Flink 一个比较重要的特性就是其支持有状态计算。即你可以将中间的计算结果进行保存,并提供给后续的计算使用: 具体而言,Flink 又将状态 (State) 分为 Keyed State 与 Operator State: 1.1 算子状态 算子状态 (Operator State):顾名思义…

Vue Router的进阶

进阶 导航守卫 官方文档上面描述的会比较深奥&#xff0c;而守卫类型也比较多&#xff0c;其中包含了全局前置守卫、全局解析守卫、全局后置钩子、路由独享守卫、组件内守卫。每一种守卫的作用和用法都不相同。这会使得大家去学习的时候觉得比较困难&#xff0c;这边主要介绍…

北邮22级信通院数电:Verilog-FPGA(5)第四第五周实验 密码保险箱的设计

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 一.密码箱的功能和安全性 显示&#xff1a;…

VxeTable 表格组件推荐

VxeTable 表格组件推荐 https://vxetable.cn 在前端开发中&#xff0c;表格组件是不可或缺的一部分&#xff0c;它们用于展示和管理数据&#xff0c;为用户提供了重要的数据交互功能。VxeTable 是一个优秀的 Vue 表格组件&#xff0c;它提供了丰富的功能和灵活的配置选项&…

更新Xcode 版本后运行项目出现错误 Unable to boot the Simulator 解决方法

错误截图 出现 Unable to boot the Simulator 错误原因很多&#xff0c;以下方法不一定都适用&#xff0c;我是通过以下方法解决的 打开命令终端输入以下命令&#xff0c;可能需要你输入开机密码 sudo rm -rf ~/Library/Developer/CoreSimulator/Caches

戏剧影视设计制作虚拟仿真培训课件提升学生的参与感

说起影视制作&#xff0c;知名的影视制片人寥寥无几&#xff0c;大多数人还在依靠摄影机拍摄实景或搭建实体场景来不断精进场景布局和导演效果&#xff0c;成本高、投入人员多且周期长&#xff0c;随着VR虚拟现实技术的不断发展&#xff0c;利用VR模拟仿真技术进行影视制作实操…

SpringBoot的流浪宠物系统

采用技术:springbootvue 项目可以完美运行

DALL·E 3 ChatGPT-4的梦幻联动

核心内容&#xff1a;DALLE 3 & ChatGPT-4的梦幻联动 hello&#xff0c;我是小索奇&#xff0c;最近DALL结合ChatGPT4的话题逐渐上升了起来&#xff0c;今天就带大家探索一下~ DALLE的主要功能是根据文本描述来生成图片。你可以告诉它一个穿着皮草的西瓜&#xff0c;它就能…

百度小程序制作源码 百度引流做关键词排名之技巧

百度作为国内最大的搜索引擎&#xff0c;对于关键词排名和流量获取的策略格外重要&#xff0c;下面给大家分享一个百度小程序制作源码和做百度引流、关键词排名的一些技巧。 移动设备的普及和微信小程序的火热&#xff0c;百度也推出了自己的小程序。百度小程序与微信小程序类…

充电保护芯片TP4054国产替代完全兼容DP4054DP4054H 锂电充电芯片

■产品概述 DP4054H是-款完整的采用恒定电流/恒定电压单节锂离子电池充电管理芯片。其SOT小封装和较少的外部元件数目使其成为便携式应用的理想器件&#xff0c;DP4054H可 以适合USB电源和适配器电源工作。 由于采用了内部PMOSFET架构&#xff0c;加上防倒充电路,所以不需要外…

机器学习基础-数据分析:房价预测

mac设置中文字体 #要设置下面两行才能显示中文 Arial Unicode MS 为字体 plt.rcParams[font.sans-serif] [Arial Unicode MS] #设置图片大小 plt.figure(figsize(20, 11), dpi200)pie官方文档 总体代码 python import pandas as pd import numpy as np import matplotlib.…