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

[C++ 网络协议] 多进程服务器端

具有代表性的并发服务器端实现模型和方法:

多进程服务器:通过创建多个进程提供服务。✔

多路复用服务器:通过捆绑并统一管理I/O对象提供服务。

多线程服务器:通过生成与客户端等量的线程提供服务。

目录

1. 进程的概念及应用

1.1 什么是进程?

1.2 创建进程

1.2.1 进程ID

1.2.2 fork函数(创建进程)

1.2.3 僵尸进程(为什么要进行进程销毁)

1.2.4 wait函数(销毁进程,阻塞)

1.2.5 waitpid函数(销毁进程,无阻塞)

1.3 进程间的通信

1.3.1 通过管道(PIPE)实现进程间的通信

2. 信号处理

2.1 signal函数(旧版本,只适用于旧版本UNIX系统)

2.2 sigaction函数(新版本,所有UNIX操作系统都一样,更稳定)

3. 实现简单的多进程服务端

3.1 代码

3.2 fork函数复制文件操作符

3.3 多进程可服务器端的缺点

4. 分割TCP的I/O程序(实现多进程客户端的一种模型)


1. 进程的概念及应用

1.1 什么是进程?

进程:占用内存空间的正在运行的程序

从操作系统的角度看,进程是程序流的基本单位。若创建多个进程,则操作系统将同时运行。有时一个程序运行过程中也会产生多个进程,多进程服务器端就是其中的代表。

CPU核的个数与进程数:

         拥有2个运算设备的CPU称作双核CPU,拥有4个运算器的称作四核CPU。也就是说,1个CPU可能包含有多个运算设备(核),核的个数与可同时运行的进程数相同。如果进程数超过了核,那么进程将分时使用CPU资源,但因为CPU运算很快,所以我们会觉得所有进程都同时在运行。

1.2 创建进程

1.2.1 进程ID

每个进程都会从操作系统中分配到一个ID,这个ID就是“进程ID”,进程ID的值为大于2的整数,因为1要分配给操作系统启动后的首个进程(协助操作系统的进程),因此用户无法得到ID为1的进程。

Linux中查看所有进程的命令: ps au

1.2.2 fork函数(创建进程)

#include<unistd.h>pid_t fork();
成功返回进程ID,失败返回-1

fork函数: 将创建基于当前运行的,调用fork函数的进程的副本(内存空间内容完全相同且互相独立)。此进程为父进程,其副本为子进程。两个进程都将执行fork函数调用后的语句(准确来说,就是从这个fork函数的返回值开始),之后的程序流中,通过fork函数的返回值来区分,当前执行的是子进程还是父进程fork函数只会复制属于进程的资源,而非操作系统的资源。

父进程:fork函数返回子进程ID

子进程:fork函数返回0

如图:

从复制发生点开始,父进程的所有变量的值,在复制发生点处时,是什么值,子进程也就会是什么值,之后两个进程之间的变量值互不影响,如图所示,最终结果:

父进程:gval=11,lval=26

子进程:gval=12,lval=25

1.2.3 僵尸进程(为什么要进行进程销毁)

如果在子进程创建和执行了销毁后父进程没有主动要求获取子进程的结束状态值,那么子进程就会成为僵尸进程,此时的进程只是一个空壳只保留了一些pid,退出状态,运行时间等数据,而其内存空间已经在return和exit的时候释放了,这就是僵尸进程。

僵尸进程产生的原因:

        子进程通过两种方式来终止,一是传递参数并调用exit函数,二是main函数找那个执行return语句并返回值。这两种方式返回的值都会传递给操作系统,但操作系统不会销毁子进程,而是直到把这些值传递给产生该子进程的父进程之后,才会进行销毁。在销毁之前,这期间状态下的进程,就是僵尸进程。所以也可得知,如果要终止子进程,那么就必须向父进程传递exit参数值或return语句的返回值。

那么子进程如何向父进程传递exit参数值或return语句的返回值,来终止自己?

答:通过父进程主动发起请求来调用。如果父进程没有主动要求获取子进程的结束状态值,那么操作系统将一直保存子进程。

为什么要销毁僵尸进程?

答:僵尸进程虽然不占用内存空间,但因其信息还在,会占用进程号,而系统所能拥有的进程号是有限的,所以如果有大量僵尸进程出现的话,则会因为进程号不够用,而导致不能创建新的进程。

1.2.4 wait函数(销毁进程,阻塞)

#include<sys/wait.h>pid_t wait(int* statloc);    //statloc指向的内存空间,存放有子进程的exit参数值或return语句的返回值等其它信息
成功返回终止的子进程ID,失败返回-1

statloc指向的内存空间,不仅仅存放有子进程的exit参数值或return语句的返回值,还有其它信息,需要通过如下宏进行分离:

WIFEXITED(int statue);
子进程正常终止返回1(真)。
WEXITSTATUS(int statue);
返回子进程的返回值。

所以,使用wait函数的标准流程:

int status;wait(&status);if(WIFEXITED(status))    //如果正常终止
{std::cout<<"子进程正常终止!"<<std::endl;std::cout<<"子进程返回值:"<<WEXITSTATUS(status)<<std::endl;
}

注意:调用wait函数时,如果没有子进程要终止,那么程序将阻塞住,直到有任意一个子进程终止。

1.2.5 waitpid函数(销毁进程,无阻塞)

#include<sys/wait.h>pid_t waitpid(
pid_t pid,         //如果为-1,那么和wait函数一样,等待任意一个子进程终止
int* statloc,      //同wait函数一样
int options        //传递头文件中声明的常量WNOHANG,表示即使没有子进程终止,//也不会阻塞程序执行,只是返回0并退出函数
);
成功返回终止的子进程ID(或0),失败返回-1

注意:返回0的情况是没有子进程终止。

1.3 进程间的通信

进程间的通信就是让两个进程间可以交换数据。

1.3.1 通过管道(PIPE)实现进程间的通信

管道与套接字一样是属于操作系统的,而非进程资源,所以在执行fork时,不会被复制。

创建管道的函数:

#include<unistd.h>
int pipe(int filedes[2]);
//filedes[0]:通过管道接收数据时使用的文件描述符,即管道出口
//filedes[1]:通过管道传输数据时使用的文件描述符,即管道入口
成功返回0,失败返回-1

父进程调用该函数创建管道时,会同时获取对应出入口的文件描述符,此时父进程可以读写同一管道。

但此时,子进程如何获取对应出入口的文件描述符呢?

答:使用fork函数让子进程获取对应管道出入口的文件描述符

int fds[2];
pipe(fds);
pid_t pid=fork();
if(pid==0)        //子进程
{write(fds[1],str,sizeof(str));
}
else              //父进程
{read(fds[0],buf,sizeof(buf));
}

此时,父进程通过fork创建了一个子进程,子进程获得了pipe函数返回的在fds里的两个文件描述符,分别是管道的出口和入口,这样的话,子进程就可以通过管道入口,写入数据,父进程就可以通过管道出口,读取数据。

两进程间管道的双向传输模型:

代码:

#include<iostream>
#include<unistd.h>int main()
{int fds[2];char buf[1024]={"who are you"};char str[1024]={"Thank you for your message"};pipe(fds);pid_t pid=fork();if(pid==0){write(fds[1],buf,sizeof(buf));sleep(2);//(1)char readbuf[1024];read(fds[0],readbuf,sizeof(readbuf));std::cout<<"父进程传来的信息:"<<readbuf<<std::endl;}else{char readbuf[1024];read(fds[0],readbuf,sizeof(readbuf));std::cout<<"子进程传来的信息:"<<readbuf<<std::endl;write(fds[1],str,sizeof(str));sleep(3);//(2)}return 0;
}

调用sleep的原因:

        (1):注释掉会产生如下结果:

子进程读取到的父进程的信息是,自己write函数写进去的消息,然后父进程会因为调用read函数没有读取到数据而阻塞住。这是因为:向管道传输数据时,先读的进程会把数据取走。当数据进入管道时,此数据就成为了无主数据,哪个进程先read那么哪个进程就能先取走。

        (2):父进程睡眠3s是为了让父进程慢于子进程结束,因为如果父进程先结束,那么当前的命令提示符窗口就会结束,然后当子进程读取到父进程发来的信息时,就会再次弹出一个命令提示符窗口。如图:

由以上问题可知,当只有一个管道时,要进行两个进程间的双向通信,就需要控制好运行的先后顺序,这很复杂甚至因为系统的不同,不大可能实现,所以,我们该怎么进行双向通信?

答:创建2个管道。2个管道分别负责不同的数据流。

修改代码:

#include<iostream>
#include<unistd.h>int main()
{int fds_write[2];int fds_read[2];char buf[1024]={"who are you"};char str[1024]={"Thank you for your message"};pipe(fds_write);pipe(fds_read);pid_t pid=fork();if(pid==0){write(fds_write[1],buf,sizeof(buf));            //子进程向父进程写,管道入口char readbuf[1024];read(fds_read[0],readbuf,sizeof(readbuf));      //子进程从父进程读,管道出口std::cout<<"父进程传来的信息:"<<readbuf<<std::endl;}else{char readbuf[1024];read(fds_write[0],readbuf,sizeof(readbuf));     //父进程就从子进程读,管道出口std::cout<<"子进程传来的信息:"<<readbuf<<std::endl;write(fds_read[1],str,sizeof(str));             //父进程向子进程写,管道入口//sleep(3);//写不写都可以}return 0;
}

2. 信号处理

将信号处理函数注册给操作系统调用,然后将信号与信号处理函数绑定,接着当触发信号时,操作系统会调用响应的信号处理函数。这种形式有点类似于Qt的connect函数

2.1 signal函数(旧版本,只适用于旧版本UNIX系统)

#include<signal.h>void (*signal(
int signo,
void (*func)(int)   
))(int);
为了在产生信号时调用,返回之前注册的函数指针

signo参数:是要调用的特殊情况信息,也就是信号。

常量含义
SIGALRM

当alarm函数调用的时间到了后,就调用信号处理函数。

#include<unistd.h>
unsigned int alarm(unsigned int seconds);
//返回0或以秒为单位的距SIGALARM信号发生所剩时间

注意:

1.如果seconds为0,那么就取消之前注册的信号。

2.如果没有指定该信号对应的处理函数,那么当alarm函数接收后将终止进程。

SIGINT输出CTRL+C,就调用信号处理函数。
SIGCHLD子进程终止,就调用信号处理函数。

func参数:是一个int型参数返回值为void型的函数指针,也就是信号处理函数。其中的int参数会传入注册时的signo参数。

注意:当信号发生时,会唤醒由于调用sleep函数而进入阻塞状态的进程。(因为:操作系统无法调用进入睡眠状态的进程的函数,所以只能先唤醒这个进程,再调用。)

2.2 sigaction函数(新版本,所有UNIX操作系统都一样,更稳定)

#include<signal.h>int sigaction(
int signo,                    //与signal函数相同,传递信号信息
const struct sigaction* act,  //对应于第一个参数的信号处理函数(信号处理器)信息
struct sigaction* oldact      //通过此参数获取之前注册的信号处理函数指针,不需要则传0
);
成功返回0,失败返回-1struct sigaction
{
void (*sa_handler)(int);     //保存信号处理函数的指针
sigset_t sa_mast;            //用于指定信号相关的选项和特性,可以用sigemptyset(&sa_mast)函数来初始化为0。
int sa_flags;                //用于指定信号相关的选项和特性,目前初始化为0即可
}

这个函数还有很多可以深究的地方,目前用到的已经可以写一个多进程服务器端了,后续的后面补上。

3. 实现简单的多进程服务端

3.1 代码

思路:

        1.父进程调用accpet函数循环接收连接请求,然后每个子进程就是一个服务器端,处理一个客户端的连接请求。

        2.此时获取的套接字文件描述符创建并传递给子进程。

        3.子进程提供服务。

#include<iostream>
#include<sys/socket.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<cstring>
#define MAX_SIZE 1024void childprocesshandle(int sig)
{std::cout<<"here"<<std::endl;if(sig==SIGCHLD){int statue;int childPid=waitpid(-1,&statue,WNOHANG);if(childPid==0){std::cout<<"没有要结束的子进程"<<std::endl;return;}if(WIFEXITED(statue))           //子进程正常返回{std::cout<<"结束的子进程ID:"<<childPid<<std::endl;std::cout<<"子进程返回的值:"<<WEXITSTATUS(statue)<<std::endl;}else{std::cout<<"子进程非正常退出"<<std::endl;}}
}int main()
{int socketfd=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);if(socketfd==-1){std::cout<<"socket fail!"<<std::endl;return 0;}//这里是复习前面所学的多种套接字选项所写int sendbufsize;socklen_t sendlent=sizeof(sendbufsize);getsockopt(socketfd,SOL_SOCKET,SO_SNDBUF,(void*)&sendbufsize,&sendlent);std::cout<<"输出缓冲区大小:"<<sendbufsize<<std::endl;int rcvbufsize;socklen_t rcvlent=sizeof(rcvbufsize);getsockopt(socketfd,SOL_SOCKET,SO_RCVBUF,(void*)&rcvbufsize,&rcvlent);std::cout<<"输入缓冲区大小:"<<rcvbufsize<<std::endl;int NoTimewait=true;socklen_t NoTimelen=sizeof(NoTimewait);setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,(const void*)&NoTimewait,NoTimelen);bool Check=false;socklen_t timewaitsize=sizeof(Check);getsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,(void*)&Check,&timewaitsize);std::cout<<"Time-wait状态:"<<Check<<std::endl;sockaddr_in sockAddr;memset(&sockAddr,0,sizeof(sockAddr));sockAddr.sin_family=AF_INET;sockAddr.sin_addr.s_addr=htonl(INADDR_ANY);sockAddr.sin_port=htons(9130);     //固定端口if(-1==bind(socketfd,(sockaddr*)&sockAddr,sizeof(sockAddr))){std::cout<<"bind fail!"<<std::endl;}if(-1==listen(socketfd,5)){std::cout<<"listen fail!"<<std::endl;}struct sigaction sigstruct;sigemptyset(&sigstruct.sa_mask);sigstruct.sa_handler=childprocesshandle;sigstruct.sa_flags=0;if(-1==sigaction(SIGCHLD,&sigstruct,0)){std::cout<<"sigaction fail!"<<std::endl;}while(1){sockaddr_in clientAddr;memset(&clientAddr,0,sizeof(clientAddr));socklen_t clientAddrLen=sizeof(clientAddr);int clientfd=accept(socketfd,(sockaddr*)&clientAddr,&clientAddrLen);if(clientfd==-1){continue;}pid_t pid=fork();        //(1)if(pid==-1){std::cout<<"fork fail!"<<std::endl;continue;}else if(pid==0){close(socketfd);std::cout<<"客户端的IP地址:"<<inet_ntoa(clientAddr.sin_addr)<<std::endl;char readbuf[MAX_SIZE];int bufsize;while((bufsize=read(clientfd,readbuf,MAX_SIZE))!=0){std::cout<<"客户端发来的信息:"<<readbuf<<std::endl;write(clientfd,readbuf,bufsize);}close(clientfd);std::cout<<"here1"<<std::endl;return 12;}elseclose(clientfd);}close(socketfd);
}

3.2 fork函数复制文件操作符

在代码中的(1)处,执行fork函数,创建子进程的时候,子进程会复制父进程的所有资源,所以,那么套接字是否复制了?

答:套接字是属于操作系统的,严格意义上来说,没有复制,只是进程会拥有相应套接字的文件描述符。

如图所示,当父进程和子进程都有一个文件描述符指向操作系统的套接字的时候,只有当2个文件描述符都终止后,才能完全销毁套接字。如果是如图的状态:

套接字不会被销毁,所以,在调用fork函数后,要将无关的套接字文件描述符关掉。 

3.3 多进程服务器端的缺点

        创建进程需要付出极大的代价,大量的运算和内存空间,由于每个进程都拥有独立的内存空间,所以在相互间的数据交换也要求采用相对复杂的方法(IPC属于相对复杂的通信方法,如管道这种方式)。所以如果每有一个客户端就创建一个进程,这是很耗费资源的。

4. 分割TCP的I/O程序(实现多进程客户端的一种模型)

其实就是将客户端的I/O分离开来,父进程负责读,子进程负责写。如图:

为什么要这样做?

答:以回声服务器端和客户端为例,回声客户端在发送完数据后,只能等待服务器端那边将数据传回来之后,才能再次发送,而将I/O分离开来,则无需等待,可多次发送,如图,左边是没分割I/O的回声客户端,右边是分割的。

综上,I/O分割有如下优点:1. 程序实现更简单         2.可以提高频繁交换数据的程序性能。

相关文章:

[C++ 网络协议] 多进程服务器端

具有代表性的并发服务器端实现模型和方法&#xff1a; 多进程服务器&#xff1a;通过创建多个进程提供服务。✔ 多路复用服务器&#xff1a;通过捆绑并统一管理I/O对象提供服务。 多线程服务器&#xff1a;通过生成与客户端等量的线程提供服务。 目录 1. 进程的概念及应用 1.…...

李宏毅 2022机器学习 HW2 strong baseline 上分路线

strong baseline上分路线 baseline增加concat_nframes &#xff08;提升明显&#xff09;增加batchnormalization 和 dropout增加hidden layer宽度至512 &#xff08;提升明显&#xff09; 提交文件命名规则为 prediction_{concat_nframes}[{n_hidden_layers}{dropout}_bn].c…...

伦敦银交易时间怎么选择?

伦敦银和伦敦金都是全球性的交易品种&#xff0c;一般的现货贵金属交易平台&#xff0c;都可以同时经营这两个品种&#xff0c;而且它们的交易时间是一致的&#xff0c;以香港市场的平台为例&#xff0c;基本上交易时间都会从北京周一的早上7点&#xff0c;延续到周六凌晨5点左…...

解决FreeRTOS程序跑不起来,打印调试却提示“Error:..\FreeRTOS\port\RVDS\ARM_CM3\port.c,244“的方法

前言 今天来分享一个不会造成程序编译报错&#xff0c;但会使程序一直跑不起来&#xff0c;并且通过调试会发现有输出错误提示的错误例子分析&#xff0c;话不多说&#xff0c;我们就直接开始分析~ 首先&#xff0c;我们说过这个例子在编译时候没有明示的错误提示&#xff0c…...

Python序列类型

序列&#xff08;Sequence&#xff09;是有顺序的数据列&#xff0c;Python 有三种基本序列类型&#xff1a;list, tuple 和 range 对象&#xff0c;序列&#xff08;Sequence&#xff09;是有顺序的数据列&#xff0c;二进制数据&#xff08;bytes&#xff09; 和 文本字符串&…...

【python爬虫】5.爬虫实操(歌词爬取)

文章目录 前言项目&#xff1a;寻找周杰伦分析过程代码实现重新分析过程什么是NetworkNetwork怎么用什么是XHR&#xff1f;XHR怎么请求&#xff1f;json是什么&#xff1f;json数据如何解析&#xff1f;实操&#xff1a;完成代码实现 一个总结一个复习 前言 这关让我们一起来寻…...

浅探Android 逆向前景趋势~

前段时间&#xff0c;我和朋友偶然间谈起安卓逆向&#xff0c;他问我安卓逆向具体是什么&#xff0c;能给我们带来什么实质性的东西&#xff0c;我也和朋友大概的说了一下&#xff0c;今天在这里拿出来和大家讨论讨论&#xff0c;也希望帮助大家来了解安卓逆向。 谈起安卓逆向…...

国际音标学习笔记

目录 1.单元音2.双元音3.辅音4.音节5.自然拼读法则5.1辅音字母的音标 1.单元音 我觉得单纯的音标并不好记住&#xff0c;所以就跟着老师整&#xff0c;根据单词记住音标的发音&#xff0c;以下是我的理解 音标对应的单词汉化iis衣əer饿ɔorigin奥u/ʊwoman五ʌart啊eanything哎…...

Azure - AzCopy学习

使用 AzCopy 将本地数据迁移到云存储空间 azcopy login 创建存储账号 ./azcopy login --tenant-id 40242385-c249-4746-95dc-4a0b64d49dc5这里的—tenant-id 在下面的地方查看&#xff1a;目录 ID&#xff1b;需要拥有Storage Blob Data Owner 的权限账号下可能会有很多目录&am…...

解决无法远程连接MySQL服务的问题

① 设置MySQL中root用户的权限&#xff1a; [rootnginx-dev etc]# mysql -uroot -pRoot123 mysql> use mysql; mysql> GRANT ALL PRIVILEGES ON *.* TO root% IDENTIFIED BY Root123 WITH GRANT OPTION; mysql> select host,user,authentication_string from user; -…...

mybatiplus代码生成器

目录 1.pom文件引入 2.引入模板引擎 3.注意 新版本&#xff0c;老版本配置和用法都不太一样&#xff0c;此处暂不展示&#xff1b;另外也可以尝试一下MyBatis-Flex 总之mybatisplus有的或者收费的&#xff0c;它都有MyBatis-Flex 是什么 - MyBatis-Flex 官方网站 1.pom文件…...

leetcode分类刷题:哈希表(Hash Table)(三、循环存在问题)

1、当需要快速判断某元素是否出现在序列中时&#xff0c;就要用到哈希表了。 2、本文针对的总结题型为给定的序列或需要构造的序列中是否存在循环&#xff0c;与 160. 相交链表、 141. 环形链表、142. 环形链表 II的题型一样。 202. 快乐数 这道题还考察如何对正整数求解各个位…...

43、基于 springboot 自动配置的 spring mvc 错误处理,就是演示项目报错后,跳转到自定义的错误页面

Spring MVC 的错误处理&#xff1a;基于 SpringBoot 自动配置之后的 Spring MVC 错误处理。 就是访问方法时出错&#xff0c;然后弄个自定义的错误页面进行显示。 ★ 两种错误处理方式 方式一&#xff1a; 基于Spring Boot自动配置的错误处理方式&#xff0c;只要通过属性文件…...

干货分享,现代列式数据库系统如何设计与实现? | StoneData 论文选读

作者&#xff1a;袁洋 | StoneData 技术架构师 审核&#xff1a;王博 论文链接&#xff1a;columnstoresfntdbs.pdf (harvard.edu) 列存四先驱和 MIT 知名教授 Samuel Madden 于 2013 年在某期刊上写的一篇当时列存相关技术的综述。文章还挺全面也很经典&#xff0c;通过剖析三…...

说说构建流批一体准实时数仓

分析&回答 基于 Hive 的离线数仓往往是企业大数据生产系统中不可缺少的一环。Hive 数仓有很高的成熟度和稳定性&#xff0c;但由于它是离线的&#xff0c;延时很大。在一些对延时要求比较高的场景&#xff0c;需要另外搭建基于 Flink 的实时数仓&#xff0c;将链路延时降低…...

北京筑龙受邀出席中物联“采购供应链中国行—走进雄安”活动

日前&#xff0c;“采购供应链中国行—走进雄安”活动在河北雄安新区成功举办&#xff0c;来自30家相关单位的50余名领导和代表参加了本次活动。活动由中国物流与采购联合会公共采购分会主办&#xff0c;中国物流与采购联合会采购委、中国雄安集团有限公司、河北雄安新区招标投…...

【Tkinter界面:练习-01】窗口-部件-布局

一、说明 python在用户界面开发中&#xff0c;其中有QT5&#xff0c;和Tkinter&#xff1b;对于实际项目&#xff0c;界面需要高大上&#xff0c;因此用QT5&#xff0c;对于开发人员的演示程序&#xff0c;或简单程序中&#xff0c;不建议QT5&#xff1b;用Tkinter已经足够。本…...

LeetCode每日一题:823. 带因子的二叉树(2023.8.29 C++)

目录 823. 带因子的二叉树 题目描述&#xff1a; 实现代码与解析&#xff1a; dp hash 原理思路&#xff1a; 823. 带因子的二叉树 题目描述&#xff1a; 给出一个含有不重复整数元素的数组 arr &#xff0c;每个整数 arr[i] 均大于 1。 用这些整数来构建二叉树&#x…...

【教学类-35-01】学号+姓名+班级(描字帖)A4一页

背景说明&#xff1a; 本学期我带机动班&#xff0c;其中大4班去的频率比较高&#xff0c;与是我用大四班的名单做了一份 “描字帖”&#xff0c;在9月1日第一天见面时&#xff0c;孩子们用记号笔描字帖时&#xff0c;我也可以对这些孩子初步混个眼熟&#xff08;聪明的&#x…...

UE5 里的一些常用的了解

# ACharacter、APawn的继承关系 ACharacter -继承自-> APawn -继承自-> AActor和 INavAgentInterface AActor -继承自-> UObject -继承自->UObjectBaseUtility -继承自-> UObjectBase&#xff08;一个独立的类&#xff09;INavAgentInterface是一个独立的类 #…...

【网络安全带你练爬虫-100练】第19练:使用python打开exe文件

目录 一、目标1&#xff1a;调用exe文件 二、目标2&#xff1a;调用exe打开文件 一、目标1&#xff1a;调用exe文件 1、subprocess 模块允许在 Python 中启动一个新的进程&#xff0c;并与其进行交互 2、subprocess.run() 函数来启动exe文件 3、subprocess.run(["文件路…...

【2D/3D RRT* 算法】使用快速探索随机树进行最佳路径规划(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

用反射实现自定义Java对象转化为json工具类

传入一个object类型的对象获取该对象的class类getFields方法获取该类的所有属性对属性进行遍历&#xff0c;并且拼接成Json格式的字符串&#xff0c;注意&#xff1a;通过属性名来推断方法名获取Method实例通过invoke方法调用 public static String objectToJsonUtil(Object o…...

rk3568 nvme硬盘分区,格式化,挂载测试

前言 环境介绍&#xff1a; 1.编译环境 Ubuntu 18.04.5 LTS 2.SDK rk356x_linux 3.单板 迅为itop-3568开发板 自制底板 一、查看硬盘 插上硬盘上电&#xff0c;进入系统后通过命令lspci查看nvme硬盘识别情况 [rootRK356X:/]# lspci -k 21:00.0 Class 0108: 1e4b:1202…...

Failed to load ApplicationContext解决办法,spring版本问题

有如下报错&#xff1a; "D:\Program Files\Java\jdk-13.0.1\bin\java.exe" -agentlib:jdwptransportdt_socket,address127.0.0.1:7325,suspendy,servern -ea -Didea.test.cyclic.buffer.size1048576 -Dfile.encodingUTF-8 -classpath "D:\Program Files\JetBr…...

Is f(z)=1/z truly an analytic function

https://math.stackexchange.com/questions/755566/is-fz-1-z-truly-an-analytic-function...

代理模式 静态代理和动态代理(jdk、cglib)——Java入职第十一天

一、代理模式 一个类代表另一个类去完成扩展功能,在主体类的基础上,新增一个代理类,扩展主体类功能,不影响主体,完成额外功能。比如买车票,可以去代理点买,不用去火车站,主要包括静态代理和动态代理两种模式。 代理类中包含了主体类 二、静态代理 无法根据业务扩展,…...

Remmina在ubuntu22.04中无法连接Windows

Remmina在ubuntu22.04中无法连接Windows 问题 提示为&#xff1a; 无法通过TLS到RDP服务器… 分析 原因是Remmina需要使用openssl通过RDP加密与Windows计算机连接&#xff0c;而ubuntu22.04系统中OpenSSL版本为3.0&#xff0c;Openssl3 将 tls<1.2 和 sha1 的默认安全级别…...

【uniapp】this有时为啥打印的是undefined?(箭头函数修改this)

&#x1f609;博主&#xff1a;初映CY的前说(前端领域) ,&#x1f4d2;本文核心&#xff1a;uniapp中this指向问题 前言&#xff1a;this大家知道是我们当前项目的实例&#xff0c;我们可以在这个this上面拿到我们原型上的全部数据。这个常用在我们在方法中调用其他方法使用。 …...

2023高教社杯数学建模思路 - 复盘:光照强度计算的优化模型

文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米&#xff0c;宽为12米&…...