网络-多路io

news2024/9/22 7:29:28

了 fcntl 函数来操作文件描述符的状态标志,其中主要是为了设置非阻塞模式。下面是对 fcntl 函数及其参数的详细解释:

fcntl 函数

fcntl 是一个用于操作文件描述符的系统调用,可以用来设置或获取文件描述符的各种属性。其原型如下:

int fcntl(int fd, int cmd, ... /* arg */ );

  • fd: 需要操作的文件描述符。
  • cmd: 操作类型(命令),指定要执行的操作。
  • arg: 额外参数,根据 cmd 的不同可能需要。

参数解释

F_GETFL 和 F_SETFL
  • F_GETFL: 获取文件描述符的当前状态标志。这个命令将返回文件描述符的当前状态标志。
  • F_SETFL: 设置文件描述符的状态标志。这个命令将文件描述符的状态标志设置为指定的值。
代码分析
int flag = fcntl(fd_r, F_GETFL);
fcntl(fd_r, F_SETFL, flag | O_NONBLOCK);

  1. int flag = fcntl(fd_r, F_GETFL);

    • 获取文件描述符 fd_r 的当前状态标志,并存储在变量 flag 中。
    • F_GETFL 命令返回的状态标志包括文件描述符的访问模式(如读、写)和一些状态标志(如是否为非阻塞模式)。
  2. fcntl(fd_r, F_SETFL, flag | O_NONBLOCK);

    • 使用 F_SETFL 命令设置文件描述符 fd_r 的状态标志。
    • flag | O_NONBLOCK 表示将 O_NONBLOCK 标志添加到当前状态标志中。
    • O_NONBLOCK 是一个标志,用于设置文件描述符为非阻塞模式。非阻塞模式意味着读写操作不会阻塞进程,如果无法立即完成操作,系统调用会立即返回,而不是等待。
flag = fcntl(0, F_GETFL);
fcntl(0, F_SETFL, flag | O_NONBLOCK);

  1. flag = fcntl(0, F_GETFL);

    • 获取标准输入(文件描述符 0)的当前状态标志,并存储在变量 flag 中。
  2. fcntl(0, F_SETFL, flag | O_NONBLOCK);

    • 使用 F_SETFL 命令设置标准输入的状态标志。
    • flag | O_NONBLOCK 表示将 O_NONBLOCK 标志添加到标准输入的当前状态标志中。
    • 这将把标准输入设置为非阻塞模式,使得读取标准输入的操作不会阻塞进程。

总结

  • fcntl(fd_r, F_GETFL) 用于获取文件描述符 fd_r 的当前状态标志。
  • fcntl(fd_r, F_SETFL, flag | O_NONBLOCK) 用于将 fd_r 的状态标志设置为非阻塞模式,确保对 fd_r 的读写操作不会阻塞。
  • 同样,fcntl(0, F_GETFL) 和 fcntl(0, F_SETFL, flag | O_NONBLOCK) 将标准输入(文件描述符 0)设置为非阻塞模式,使得读取标准输入的操作也是非阻塞的。

通过这些设置,你可以确保读写操作在没有数据时不会导致程序阻塞,从而使程序能够在执行其他任务时保持响应。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>

int main(int argc, char *argv[])
{

    int ret = mkfifo("myfifo1",0666);
    if(-1 == ret)
    {
        if( EEXIST!= errno )
        {
            perror("mkfifo");
            return 1;
        }
    }
  int fd_r = open("myfifo1",O_RDONLY);
    if(-1 == fd_r)
    {
        perror("open");
        return 1;
    }

    int flag = fcntl(fd_r,F_GETFL);
    fcntl(fd_r,F_SETFL,flag|O_NONBLOCK);

    flag = fcntl( 0,F_GETFL);
    fcntl(0,F_SETFL,flag|O_NONBLOCK);
    while(1)
    {
        char buf[128]={0};
        if(read(fd_r,buf,sizeof(buf))>0)
        {
            printf("fifo:%s\n",buf);
        }

        bzero(buf,sizeof(buf));
        if(fgets(buf,sizeof(buf),stdin))
        {
            printf("terminal:%s\n",buf);
        }
    }
    return 0;
}

signal函数在多路io

演示了如何使用信号驱动 I/O(SIGIO)来处理命名管道(FIFO)的异步事件。下面是对代码及各个函数的作用的详细解释:

代码解析

1. #include 和 全局变量
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>

int fd_r;

  • #include:引入了必要的头文件,提供了函数原型和常量定义。
  • fd_r:定义一个全局变量,用于存储打开的 FIFO 文件描述符。
2. handle 函数
void handle(int num)
{
    char buf[128]={0};
    read(fd_r,buf,sizeof(buf));
    printf("fifo:%s\n",buf);
    return ;
}

  • handle:信号处理函数,当收到 SIGIO 信号时被调用。
    • num:传递给信号处理函数的信号编号,这里没有用到。
    • read(fd_r, buf, sizeof(buf)):从 FIFO 中读取数据到 buf
    • printf("fifo:%s\n", buf):打印读取到的数据。
3. main 函数
int main(int argc, char *argv[])
{
    signal(SIGIO, handle);

  • signal(SIGIO, handle):设置信号处理函数,当进程接收到 SIGIO 信号时,会调用 handle 函数。
    int ret = mkfifo("myfifo1",0666);
    if(-1 == ret)
    {
        if( EEXIST!= errno )
        {
            perror("mkfifo");
            return 1;
        }
    }

  • mkfifo("myfifo1", 0666):创建一个名为 "myfifo1" 的 FIFO(命名管道)。如果文件已存在 (EEXIST),则不处理错误。
    fd_r = open("myfifo1", O_RDONLY);
    if(-1 == fd_r)
    {
        perror("open");
        return 1;
    }

  • open("myfifo1", O_RDONLY):以只读模式打开 FIFO,并将文件描述符存储在 fd_r 中。如果打开失败,打印错误信息并退出。
    int flag = fcntl(fd_r, F_GETFL);
    fcntl(fd_r, F_SETFL, flag | O_ASYNC);

  • fcntl(fd_r, F_GETFL):获取当前文件描述符的状态标志。
  • fcntl(fd_r, F_SETFL, flag | O_ASYNC):设置文件描述符为异步模式 (O_ASYNC)。这意味着当文件描述符有数据可读时,会向进程发送 SIGIO 信号。
    fcntl(fd_r, F_SETOWN, getpid());

  • fcntl(fd_r, F_SETOWN, getpid()):设置 fd_r 文件描述符的信号所有者为当前进程(getpid())。这使得 SIGIO 信号会发送到当前进程。
    while(1)
    {
        char buf[128]={0};
        bzero(buf, sizeof(buf));
        fgets(buf, sizeof(buf), stdin);
        printf("terminal:%s\n", buf);
    }
    return 0;
}

  • 无限循环
    • fgets(buf, sizeof(buf), stdin):从标准输入读取数据到 buf
    • printf("terminal:%s\n", buf):打印从标准输入读取的数据。

主要函数的作用

  1. signal(SIGIO, handle)

    • 用于设置进程在接收到 SIGIO 信号时调用 handle 函数。
  2. mkfifo("myfifo1", 0666)

    • 创建一个命名管道(FIFO)。如果已存在,则不处理错误。
  3. open("myfifo1", O_RDONLY)

    • 打开创建的 FIFO 文件,以只读模式获取文件描述符。
  4. fcntl(fd_r, F_GETFL) 和 fcntl(fd_r, F_SETFL, flag | O_ASYNC)

    • 获取和设置文件描述符的状态标志,使其处于异步模式。异步模式会使文件描述符触发信号(SIGIO),当数据到达时通知进程。
  5. fcntl(fd_r, F_SETOWN, getpid())

    • 设置文件描述符 fd_r 的信号所有者为当前进程,使得 SIGIO 信号会发送到当前进程。
  6. read(fd_r, buf, sizeof(buf))

    • 在信号处理函数中,从 FIFO 中读取数据。
  7. fgets(buf, sizeof(buf), stdin) 和 printf("terminal:%s\n", buf)

    • 在主循环中,读取标准输入的数据并打印。

总结

这段代码通过设置文件描述符 fd_r 为异步模式,并为其指定信号所有者,使得当有数据可读时会发送 SIGIO 信号。信号处理函数 handle 会在收到 SIGIO 时被调用,从 FIFO 中读取数据并打印。这种方式允许进程在等待数据的同时执行其他操作,比如读取标准输入。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
int fd_r;
void handle(int num)
{
    char buf[128]={0};
    read(fd_r,buf,sizeof(buf));
    printf("fifo:%s\n",buf);
    return ;
}
int main(int argc, char *argv[])
{
    signal(SIGIO,handle);

    int ret = mkfifo("myfifo1",0666);
    if(-1 == ret)
    {
        if( EEXIST!= errno )
        {
            perror("mkfifo");
            return 1;
        }
    }
    fd_r = open("myfifo1",O_RDONLY);
    if(-1 == fd_r)
    {
        perror("open");
        return 1;
    }

    //给管道设置信号驱动
    int flag = fcntl(fd_r,F_GETFL);
    fcntl(fd_r,F_SETFL,flag| O_ASYNC);
    //如果有写管道,本进程作为sigio信号的接收者
    fcntl(fd_r,F_SETOWN,getpid());
    while(1)
    {
        char buf[128]={0};
        bzero(buf,sizeof(buf));
        fgets(buf,sizeof(buf),stdin);
        printf("terminal:%s\n",buf);
        
    }
    return 0;
}

select 函数用于监视多个文件描述符的状态,以便在其中一个或多个文件描述符变为可读、可写或出现异常时进行处理。下面是对 select 相关函数和操作的详细解释。

select 函数

select 函数用于监视文件描述符集合,以确定哪些文件描述符在某个时间点是就绪的(即,可以进行读、写操作或出现异常)。其原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

  • nfds: 文件描述符的数量+1。它通常是你感兴趣的最大文件描述符值加1。
  • readfds: 监视的文件描述符集合(用于读操作)。
  • writefds: 监视的文件描述符集合(用于写操作)。
  • exceptfds: 监视的文件描述符集合(用于异常条件)。
  • timeout: 指定 select 函数的超时时间。如果超时时间为 NULL,则 select 将无限期等待直到有文件描述符变为可用。

fd_set 操作函数

  • FD_ZERO(fd_set *set): 清空文件描述符集合 set
  • FD_SET(int fd, fd_set *set): 将文件描述符 fd 添加到文件描述符集合 set 中。
  • FD_CLR(int fd, fd_set *set): 从文件描述符集合 set 中移除文件描述符 fd
  • FD_ISSET(int fd, fd_set *set): 检查文件描述符 fd 是否在文件描述符集合 set 中。

代码分析

// 1. 创建文件描述符集合
fd_set rd_set, tmp_set; // 读集合
FD_ZERO(&rd_set);       // 清空读集合
FD_ZERO(&tmp_set);      // 清空临时集合

// 2. 添加文件描述符
FD_SET(0, &tmp_set);    // 将标准输入(文件描述符 0)添加到临时集合
FD_SET(fd_r, &tmp_set); // 将文件描述符 fd_r 添加到临时集合

while (1) {
    // 6. 复制临时集合到读集合
    rd_set = tmp_set;
    char buf[128] = {0};

    // 3. 等待事件发生
    select(fd_r + 1, &rd_set, NULL, NULL, NULL);

    // 4. 查找发生事件的文件描述符
    if (FD_ISSET(fd_r, &rd_set)) {
        read(fd_r, buf, sizeof(buf));
        printf("fifo: %s\n", buf);
    }

    if (FD_ISSET(0, &rd_set)) {
        bzero(buf, sizeof(buf));
        fgets(buf, sizeof(buf), stdin);
        printf("terminal: %s\n", buf);
    }
}

  1. 创建文件描述符集合

    • fd_set rd_set, tmp_set; 定义了两个 fd_set 类型的变量,用于存储文件描述符集合。
    • FD_ZERO(&rd_set); 清空 rd_set 集合。
    • FD_ZERO(&tmp_set); 清空 tmp_set 集合。
  2. 添加文件描述符

    • FD_SET(0, &tmp_set); 将标准输入(文件描述符 0)添加到 tmp_set 集合中。
    • FD_SET(fd_r, &tmp_set); 将文件描述符 fd_r 添加到 tmp_set 集合中。
  3. 等待事件

    • rd_set = tmp_set; 复制 tmp_set 到 rd_setrd_set 用于 select 调用,避免在 select 调用后丢失对文件描述符的跟踪。
    • select(fd_r + 1, &rd_set, NULL, NULL, NULL); 等待直到 fd_r 或标准输入有数据可读。fd_r + 1 是需要监视的文件描述符的数量加1。
  4. 处理事件

    • FD_ISSET(fd_r, &rd_set) 检查 fd_r 是否在 rd_set 中,即检查 fd_r 是否有数据可读。如果有,读取数据并打印。
    • FD_ISSET(0, &rd_set) 检查标准输入是否在 rd_set 中,即检查标准输入是否有数据可读。如果有,读取数据并打印。

总结

  • select 用于监视多个文件描述符,以确定哪些文件描述符准备好进行读、写或异常处理。
  • fd_set 操作函数用于创建和修改文件描述符集合。
  • 在代码中,select 被用于等待标准输入或文件描述符 fd_r 中有数据可读,并根据文件描述符的状态执行相应的读取操作。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[])
{

    int ret = mkfifo("myfifo1",0666);
    if(-1 == ret)
    {
        if( EEXIST!= errno )
        {
            perror("mkfifo");
            return 1;
        }
    }
  int fd_r = open("myfifo1",O_RDONLY);
    if(-1 == fd_r)
    {
        perror("open");
        return 1;
    }
    //1 create set 
    fd_set rd_set,tmp_set; //read set 
    FD_ZERO(&rd_set);
    FD_ZERO(&tmp_set);
    
    // 2. add fd 
    FD_SET(0,&tmp_set);
    FD_SET(fd_r,&tmp_set);
    while(1)
    {
        //6.clean flag
        rd_set = tmp_set;
        char buf[128]={0};
        //3 wait event 
        select(fd_r+1,&rd_set,NULL,NULL,NULL );
        //4 找到对应的fd
        if(FD_ISSET(fd_r,&rd_set))
        {
            read(fd_r,buf,sizeof(buf));
            printf("fifo:%s\n",buf);
        }

        if(FD_ISSET(0,&rd_set))
        {
            bzero(buf,sizeof(buf));
            fgets(buf,sizeof(buf),stdin);
            printf("terminal:%s\n",buf);
        }
    }
    return 0;
}

epoll

epoll 被用来实现对两个文件描述符的事件通知机制。下面是对各个函数和操作的详细分析:

1. add_fd(int epfd, int fd)

这个函数用于将一个文件描述符 fd 添加到 epoll 实例 epfd 中,并指定感兴趣的事件类型。

  • 参数
    • epfdepoll_create 创建的 epoll 文件描述符。
    • fd:要添加到 epoll 的文件描述符。
  • 操作
    • 创建一个 epoll_event 结构体 ev,设置感兴趣的事件为 EPOLLIN,表示关注该文件描述符的可读事件。
    • 使用 epoll_ctl 函数将 fd 添加到 epoll 实例中,操作类型为 EPOLL_CTL_ADD
  • 返回值
    • 返回 epoll_ctl 的结果,如果返回 -1 表示添加失败,并调用 perror 打印错误信息。

2. main(int argc, char *argv[])

主函数的执行流程:

  1. 创建命名管道(FIFO)

    • 使用 mkfifo 创建两个命名管道 "myfifo1" 和 "myfifo2",这两个管道用于进程间通信。如果创建失败并且错误码不是 EEXIST(文件已存在),则打印错误信息并退出。
  2. 打开命名管道

    • 使用 open 打开 myfifo1(以只读模式)和 myfifo2(以只写模式),分别获得 fd_r 和 fd_w 文件描述符。如果打开失败,打印错误信息并退出。
  3. 创建 epoll 实例

    • 使用 epoll_create 创建一个 epoll 实例,并返回一个文件描述符 epfd。如果创建失败,打印错误信息并退出。
  4. 添加文件描述符到 epoll 实例

    • 调用 add_fd 函数将标准输入(文件描述符 0)和管道 myfifo1 的文件描述符 fd_r 添加到 epoll 实例中,开始监听它们的事件。
  5. 事件监听循环

    • 进入无限循环,调用 epoll_wait 等待事件发生。epoll_wait 会阻塞直到有事件发生,或者超时(这里设置为 -1,表示无限等待)。

    • 事件处理

      • 如果 epoll_wait 返回有事件发生,遍历所有的事件:
        • 如果事件的文件描述符是标准输入(0),读取输入数据并写入到 fd_w,如果输入为 "#quit\n",则退出程序。
        • 如果事件的文件描述符是 fd_r,从 fd_r 读取数据并打印。如果读取的数据是 "#quit\n" 或读取失败,则退出程序。

主要函数和操作的作用总结

  1. epoll_create:创建 epoll 实例,返回 epoll 文件描述符。此文件描述符用于后续的 epoll_ctl 和 epoll_wait 操作。

  2. epoll_ctl:对 epoll 实例进行控制操作,如添加、修改或删除文件描述符及其事件。此处用于将文件描述符添加到 epoll 实例中。

  3. epoll_wait:等待 epoll 实例中的文件描述符发生事件。阻塞直到有文件描述符的事件发生或者超时,返回发生的事件数量。

  4. fgets 和 write:用于从标准输入读取数据,并写入到 fd_w 对应的管道中。

  5. read 和 printf:用于从 fd_r 对应的管道中读取数据,并输出到标准输出。

这段代码展示了如何使用 epoll 机制来处理异步 I/O 操作,同时实现了基于命名管道的进程间通信。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <sys/epoll.h>

int add_fd(int epfd,int fd)
{
	struct epoll_event ev;
	ev.events = EPOLLIN;
	ev.data.fd = fd;
	int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev);
	if(-1==ret)
	{
		perror("add_fd");
	}
	return ret;
}

int main(int argc, char *argv[])
{

    int ret = mkfifo("myfifo1",0666);
    if(-1 == ret)
    {
        if( EEXIST!= errno )
        {
            perror("mkfifo");
            return 1;
        }
    }
    ret = mkfifo("myfifo2",0666);
    if(-1 == ret)
    {
        if( EEXIST!= errno )
        {
            perror("mkfifo");
            return 1;
        }
    }

  int fd_r = open("myfifo1",O_RDONLY);
    if(-1 == fd_r)
    {
        perror("open");
        return 1;
    }
   


     int fd_w = open("myfifo2",O_WRONLY);
    if(-1 == fd_w)
    {
		perror("open");
		return 1;
	}
	struct epoll_event rev[2];

	int epfd;
	int ep_ret;
	epfd = epoll_create(2);
	if(-1==epfd)
	{
		perror("epoll_create");
		return 1;
	}

	add_fd(epfd,0);
	add_fd(epfd,fd_r);
	while(1) 
	{
		ep_ret = epoll_wait(epfd,rev,2,-1);
		if(ep_ret>0)
		{
			int i=0;
			for(i=0;i<ep_ret;i++)
			{
				if(0==rev[i].data.fd)
				{
					printf("to A:");
					char buf[128]={0};
					fgets(buf,sizeof(buf),stdin);//#quit
					if(0 == strcmp(buf,"#quit\n"))
					{
						exit(0);
					}
					write(fd_w,buf,strlen(buf));
				}
				else if(fd_r==rev[i].data.fd)
				{
					char buf[128]={0};
					int ret = read(fd_r,buf,sizeof(buf));

					if(0==strcmp(buf,"#quit\n") || ret<=0)
					{
						exit(0);
					}
					printf("from A:%s",buf);
					fflush(stdout);
				}
			}
		}
	}
	return 0;
}

第一步 创建epoll_create

第二步  添加成员

第三步  epoll_wait()

有timeout的使用

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

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

相关文章

Django学习实战篇二(适合略有基础的新手小白学习)(从0开发项目)

前言&#xff1a; 从这一章开始&#xff0c;我们来创建项目typeidea&#xff0c;我把它放到了GitHub上。强烈建议你也到GitHub上注册一个账号&#xff08;如果没有的话&#xff09;&#xff0c;然后创建这样的项目。当然&#xff0c;你也可以起一个属于自己的名称。这个项目就是…

tensorrt plugin

自定义plugin 流程 首先明确要开发的算子&#xff0c;最好是 CUDA 实现&#xff1b;继承 IPluginV2DynamicExt / IPluginV2IOExt类实现一个Plugin 类&#xff0c;在这里调用前面实现的算子&#xff1b;继承 IPluginCreator 类实现一个 PluginCreator 类&#xff0c;用于创建插…

【数据结构取经之路】布隆过滤器BloomFilter原理、误判率推导、代码实现

目录 背景介绍 简介 布隆过滤器的实现思路 布隆过滤器的作用 布隆过滤器误判率推导过程 布隆过滤器的实现 布隆过滤器的删除问题 布隆过滤器的优缺点 布隆过滤器的应用 背景介绍 在一些场景下面&#xff0c;有大量数据需要判断是否存在&#xff0c;而这些数据不是整…

物联网之MQTT

一&#xff0c;MQTT 及其在物联网中的应用 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的消息传输协议&#xff0c;设计用于低带宽、延迟高、不稳定的网络环境&#xff0c;特别适合物联网&#xff08;IoT&#xff09;应用。它采用了发布/订…

案例 | 稳石机器人赋能日化企业内部物流提质增效

近年来&#xff0c;日化产业高速发展&#xff0c;以“清洁类”及“化妆品类”为主的日化品在人们生活中扮演着不可或缺的角色。然而在发展过程中&#xff0c;诸多难点也开始显头&#xff0c;招工难用工贵、生产速度受到掣肘等难题&#xff0c;都对日化企业可持续发展构成挑战。…

智慧安防EasyCVR视频监控汇聚管理平台云端录像时间轴拖动不跳转,是什么原因?

视频汇聚EasyCVR视频智能管理系统以其强大的拓展性、灵活的部署方式、高性能的视频能力和智能化的分析能力&#xff0c;为各行各业的视频监控需求提供了优秀的解决方案。EasyCVR平台支持多种视频流的外部分发&#xff0c;如RTMP、RTSP、HTTP-FLV、WebSocket-FLV、HLS、WebRTC、…

国产SaaS的挑战与未来:探索用户增长的新路径

在数字化转型的浪潮中&#xff0c;SaaS&#xff08;软件即服务&#xff09;行业扮演着至关重要的角色&#xff0c;为企业提供了灵活、高效的数字化解决方案。然而&#xff0c;国产SaaS行业在快速发展的同时&#xff0c;也面临着诸多挑战&#xff0c;包括客户定制化需求高、市场…

上门家政系统源码开发详解

引言 随着现代生活节奏的加快&#xff0c;越来越多的家庭选择聘请家政服务人员来解决日常生活中诸如清洁、烹饪等琐事。面对这一市场需求&#xff0c;开发一个高效的上门家政服务系统显得尤为重要。本文旨在探讨如何构建这样一个系统&#xff0c;并分享一些开发过程中需要注意的…

五、代理模式

代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它为其他对象提供一个代理以控制对这个对象的访问。代理对象通常会对真实对象的请求进行一些处理&#xff08;例如延迟初始化、访问控制、日志记录等&#xff09;&#xff0c;它能够在不改变目标…

K8s搭建过程,新手闭眼入!!!超详细教程

一、k8s搭建harbor仓库 前提&#xff1a;在另一台主机已搭建好harbor私人仓库&#xff0c;之前博客中有详细记录 环境&#xff1a;准备三台主机&#xff0c;一台master&#xff0c;一台node1&#xff0c;一台noed2 1.本地解析 将harbor镜像仓库所在的主机的域名写在所有主机…

Redis String 类型详解:操作命令、底层编码与使用案例

文章目录 一 . 常见命令1.1 set1.2 get1.3 mset、mget1.4 setnx、setex、psetex1.5 incr、incrby1.6 decr、decrby、incrbyfloat1.7 append1.8 getrange1.9 setrange1.10 strlen小结 二 . string 的编码方式三 . 应用场景3.1 缓存3.2 计数器3.3 共享会话3.4 手机验证码 Hello ,…

# 利刃出鞘_Tomcat 核心原理解析(十一)-- Tomcat 附加功能 WebSocket -- 2

利刃出鞘_Tomcat 核心原理解析&#xff08;十一&#xff09;-- Tomcat 附加功能 WebSocket – 2 一、Tomcat专题 - WebSocket - 案例 - 登录功能 1、在项目 dzs168_chat_room 中&#xff0c;导入 tomcat 项目依赖&#xff08; dzs168_chat_room/web/lib/ &#xff09; idea -…

花生壳的登录及获取二级域名

1、下载花生壳客户端 2、安装完毕 3、扫码登录 4、微信登录花生壳管理后台 5、二级域名的注册 已经帮我们自动生成了一个免费的二级域名。 我们可以用这个二级域名快速的建立网站了。

[YM]课设-C#-WebApi-Vue-员工管理系统 (七)员工统计表

前端&#xff1a; 注&#xff1a;这里主要解释下echarts组件&#xff0c;需要一定的Vue基础 emmmmm 明显能看到上面写“对不起暂未开发” 是的 这个是博主自己加上去的 but 这个统计表也是类似于Element UI的小组件 Element&#xff1a;Element - 网站快速成型工具 &am…

Quartz.Net_依赖注入

简述 有时会遇到需要在IJob实现类中依赖注入其他类或接口的情况&#xff0c;但Quartz的默认JobFactory并不能识别具有有参构造函数的IJob实现类&#xff0c;也就无法进行依赖注入 需要被依赖注入的类&#xff1a; public class TestClass {public TestClass(Type jobType, s…

Python 从入门到实战5(列表的其它操作)

我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;通过熟练掌握python基础&#xff0c;然后结合经典实例、实践相结合&#xff0c;使我们完全掌握python&#xff0c;并做到独立完成项目开发的能力。 之前的文章我们通过举例学习了python 中列表的简单操作&#xff0…

虚拟机输入ip addr不显示IP地址

本机配置 Window10 VMware Workstation 17 CentOS 7 虚拟机输入ip addr查询不到ip地址&#xff08;下图&#xff09; 解决办法&#xff1a; 查看配置文件&#xff0c;输入下面命令(用于编辑文件) vi /etc/sysconfig/network-scripts/ifcfg-ens33进入配置配置文件&#xf…

交叉编译 gmp

文章目录 交叉编译 gmp1 概述2 源码下载2.1 官网下载2.2 使用 apt source 下载 3 交叉编译4 关于 DESTDIR 的说明 交叉编译 gmp 1 概述 GMP (GNU Multiple Precision Arithmetic Library) 是一个用于任意精度计算设计的数学库&#xff0c;它的主要目标应用是密码学应用和研究…

ARP协议和DNS的工作原理

ARP协议 ARP协议的工作原理&#xff1a; 首先主机向自己的网络广播发送一个arp请求&#xff0c;请求报文包括目的端的ip地址和目的端的以太网地址。网络上的其他机器收到这个请求&#xff0c;但只有被请求的才会回应一个应答报文&#xff0c;报文中有自己的物理地址。 arp维护了…

【python因果推断库1】协方差分析(ANCOVA)用于处理前/后非等效组设计

目录 生成合成数据 分析 这是一个基于合成数据的初步示例。希望不久之后能用真实研究的数据进行更新。 在只有一次预处理测量和一次后处理测量的情况下&#xff0c;我们可以使用类似于协方差分析(ANCOVA)的方法来分析非等效组设计(NEGD)实验的数据。基本模型是&#xff1a; i指…