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

WebServer -- 定时器处理非活动连接(上)

目录

🍍函数指针

🌼基础知识

🐙整体概述

🎂基础API

sigaction 结构体

sigaction()

sigfillset()

SIGALRM, SIGTERM 信号

alarm()

socketpair()

send()

📕信号通知流程

统一事件源

信号处理机制

🌼源码分析

信号处理函数

信号通知逻辑

代码


🍍函数指针

函数指针是指向函数的指针变量。在C和C++中,函数被存储在内存中的某个位置,函数指针可以指向这个内存位置,从而允许通过指针间接调用函数

函数指针的声明方式👇

返回类型 (*指针变量名)(参数列表);

例如,如果有一个函数 void myFunction(int x)

void (*funcPtr)(int); // 声明一个指向函数的指针变量 funcPtr
funcPtr = &myFunction; // 将函数的地址赋给指针变量
// 通过函数指针调用函数
funcPtr(10); // 调用 myFunction(10)

🌼基础知识

非活跃 

客户端(即浏览器)与服务器建立连接后,长时间不交换数据,一直占用服务器的文件描述符,导致连接资源浪费

定时事件

固定一段时间后触发某段代码,由该代码处理一个事件

eg: 从内核表删除事件,并关闭文件描述符,释放连接资源

定时器

利用结构体 / 其他形式,将多种 定时事件 封装。

具体的,这里只涉及一种定时事件,即 -- 定期检测非活跃连接,

这里将该定时事件与连接资源,封装为一个 结构体定时器

定时器容器

使用某种容器类 数据结构,将上述多个定时器组合起来,便于对定时事件统一管理

比如,项目中使用 升序链表 ,将所有定时器串联起来

🐙整体概述

简介

  • 定时器处理非活动连接是一种机制,用于检测和关闭长时间没有活动的客户端连接
  • 定时器会周期性地检查连接的活动状态,并在连接超过一定时间没有任何数据传输时,将其标记为非活动连接并关闭
  1. 具体来说,当客户端与服务器建立连接后,服务器会启动一个定时器来监视该连接的活动状态
  2. 每当服务器接收到客户端发送的数据或发送数据给客户端时,定时器会被重置,表示该连接是活动的
  3. 如果在一段时间内没有任何数据传输,定时器将超时并关闭该连接

TinyWebServer 中,服务器 主循环 为每一个连接创建一个 定时器,并对每个连接进行定时

此外,升序事件链表容器,将所有定时器串联起来

若 主循环 收到定时通知,则在链表中依次执行 定时任务

Linux提供 3 种定时方法👇

  • socket 选项 SO_RECVTIMEO 和 SO_SNDTIMEO
  • SIGALRM信号
  • I / O 复用系统调用的超市参数

TinyWebServer 使用 SIGALRM 信号

具体的,利用 alarm() 函数周期性地触发 SIGALRM 信号,信号处理函数利用 管道 通知 主循环

主循环 接收到信号后,处理 升序链表 的所有定时器

若这段时间内,没有交换数据,则将该连接关闭,释放占用的资源

由此可见,定时器处理 非活动连接模块,分 2 部分:

1)定时方法 与 信号通知流程

2) 定时器 及其 容器设计与定时任务 的处理

总览

定时方法 + 信号通知流程

涉及 基础API,信号通知流程,代码实现

基础API

sigaction 结构体,SIGALRM 信号, SIGTERM 信号

函数👇

sigaction(),sigfillset(),alarm(),socketpair(),send()

信号通知流程

 统一事件源 + 信号处理机制

🎂基础API

更好的源码阅读体验~

sigaction 结构体

  • sa_handler() -- 函数指针,指向信号处理函数
  • sa_sigaction() -- 信号处理函数,3个参数,获得关于信号更详细的信息
  • sa_mask -- 信号处理函数执行期间,需要被屏蔽的信号
  • sa_falgs -- 信号处理行为
    • SA_RESTART:使被信号打断的系统调用,重新自动发起
    • SA_NOCLDSTOP:使父进程,在其子进程 暂停/继续运行 时,不会收到 SIGCHLD 信号
    • SA_NOCLDWAIT:使父进程,在其子进程 退出 时,不会收到 SIGCHLD 信号,此时 子进程 退出不会成为僵尸进程
    • SA_NODEFER:使对信号的屏蔽无效,即,在信号处理函数期间,仍能发出这个信号
    • SA_RESETHAND:信号处理之后,重新设置为默认的处理方式
    • SA_SIGINFO:使用 sa_sigaction 成员,而不是 sa_handler 作为信号处理函数
  • sa_restorer -- 一般不使用
// 定义一个结构体sigaction,用于处理信号
struct sigaction {// 指向处理信号的函数指针,接受一个int参数void (*sa_handler)(int);// 指向处理信号的函数指针,接受3个参数void (*sa_sigaction)(int, siginfo_t*, void*);// 信号掩码sigset_t sa_mask;// 标志位int sa_flags;// 恢复处理程序的函数指针,不接受参数,不返回任何值void (*sa_restorer)(void);
};

sigaction()

  • signum    操作的信号
  • act    对信号设置新的处理方式
  • oldact    信号旧的处理方式
  • 返回值,0 成功,-1 有错误发生
#include<signal.h>int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

sigfillset()

信号集

👆在 Linux 系统中,通常使用 sigset_t类型来表示信号集。这个类型通常是一个整数数组,每个元素对应一个信号,用来表示该信号是否被设置

👇将参数 set 信号集 初始化,然后把所有信号加入此信号集

#include<signal.h>int sigfillset(sigset_t *set);

SIGALRM, SIGTERM 信号

#define SIGALRM 14 // alarm 系统调用 产生timer时钟信号
#define SIGTERM 15 // 终端发送的终止信号

alarm()

设置信号传送闹钟,即,设置信号 SIGALRM,经过参数 seconds 秒后,发送给目前进程

如果未设置信号 SIGALRM 的处理函数,那么 alarm() 默认处理终止进程

#include<unistd.h>unsigned int alarm(unsigned int seconds);

socketpair()

Linux 下,使用 socketpair() 函数,创建一对套接字进行通信,TinyWebServer 使用管道通信

  • domain -- 协议族(PF_UNIX 或 AF_UNIX)
  • type -- 协议(SOCK_STREAM 或 SOCK_DGRAM),SOCK_STREAM 基于 TCP,SOCK_DGRAM 基于 UDP
  • protocol -- 类型(只能为 0)
  • sv[2] -- 套接字柄对,两个句柄作用相同,均可独写双向操作
  • 返回结果,0 成功,-1 创建失败
#include<sys/types.h>
#include<sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);

send()

套接字 发送缓冲区 变满时,send 通常会阻塞,除非 套接字 设置成 非阻塞模式

当缓冲区变满,返回 EAGAIN 或 EWOULDBLOCK 错误,此时可调用 select() 函数,来监视何时可以发送数据

#include<sys/types.h>
#include<sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);

📕信号通知流程

Linux 下信号,采用 异步处理机制,信号处理函数 和 当前进程 是 2 条不同的执行路线

异步处理机制👇解释

在Linux系统中,信号就像是一种突然发生的事件通知,比如按下Ctrl+C键发送的中断信号。当这个事件发生时,操作系统会中断当前正在进行的工作,去执行与之对应的处理函数,处理完后再回到原来的工作。

这个处理过程是异步的,也就是说,处理信号的函数和当前正在执行的程序是两条不同的路线。处理函数负责响应信号事件,而当前程序则会在收到信号时被中断,等待处理完毕后再继续执行。这样可以保证及时响应各种突发事件,确保系统的稳定和安全。

当进程收到信号时,操作系统会中断当前的正常流程,转而进入信号处理函数执行操作,完成后再返回中断的地方继续执行

为了避免 信号竞态 的发生,信号处理期间,系统不会再次触发它

所以,为确保该信号不被屏蔽太久,信号处理函数,需要尽可能快地执行完毕

信号处理函数,需要处理该信号对应的逻辑,当该逻辑较复杂,信号处理函数执行时间过长,会导致信号屏蔽太久

解决方案

信号处理函数,仅发送信号,通知程序主循环,将信号对应的处理逻辑,放在主循环中

由主循环执行信号对应的逻辑代码

统一事件源

指的是,将 信号事件 与 其他事件 一样被处理

eg:信号处理函数使用  管道  将信号传递给  主循环

信号处理函数往  管道的写端  写入信号值

主循环则从  管道的读端  读出信号值

使用 I / O 复用系统调用来监听  管道读端  的可读事件

此时,信号事件 与 其他文件描述符 都可以通过 epoll 来监测,从而实现统一处理

信号处理机制

每个进程中,都存在一个表,里面存着每种信号所代表的含义,内核通过设置表项中每一个位,来标识对应的信号类型

  • 信号接收
    • 接收信号的任务是由 内核 代理的,当内核接收到信号后,会将其放到对应进程的信号队列中,同时向进程发送一个中断,使其陷入内核态。注意,此时信号还只是在队列中,对进程来说,暂时不知道信号到来了
  • 信号检测
    • 进程从内核态返回到用户态前,进行信号检测
    • 进程在内核态中,从睡眠被唤醒时,进行信号检测
    • 进程陷入内核态后,有 2 种场景对信号进行检测
    • 当发现新信号时,会进入下一步,信号的处理
  • 信号处理
    • (内核)信号处理函数,运行在 用户态,调用处理函数前,内核会将当前的内核栈的内容备份,拷贝到用户栈上,并修改指令寄存器(eip),将其指向信号处理函数
    • (用户)接下来,进程返回用户态,执行相应的信号处理函数
    • (内核)信号处理函数 执行完毕后,还要返回内核态,检查是否还有其他信号未处理
    • (用户)所有信号处理完后,内核栈就会恢复(从用户栈的备份拷贝),同时恢复指令寄存器(eip),将其指向中断前的运行位置,最后返回用户态,继续执行进程

到此,一个完整的 信号处理流程 就结束了

如果同时有多个信号到达,上面的处理流程,会在第 2 步和第 3 步间循环

🌼源码分析

信号处理函数

自定义信号处理函数,创建 sigaction 结构体变量,设置信号函数

// 信号处理函数
void sig_handler(int sig)
{// 为保证函数的可重入性,保留原来的errno// 可重入性:中断后,再次进入该函数,环境变量与之前相同// 不会丢失数据int save_errno = errno;int msg = sig;// 信号值从 管道写端 写入,传输字符类型,而非 整型send(pipefd[1], (char*)&msg, 1, 0);// 原来的 errno 赋值为当前的 errnoerrno = save_errno;
}

 信号处理函数中,仅通过管道发送信号值,不处理信号对应的逻辑,缩短异步执行时间,减少对主程序影响

void addsig(int sig, void(handler)(int), bool restart = true);

👆解释

  1. sig:要注册的信号。
  2. handler:信号处理函数。
  3. restart:标志,指示是否在中断系统调用后自动重启该调用。

函数声明中使用了函数指针作为参数,这意味着 handler 参数必须是一个指向函数的指针,该函数接受一个整数参数并返回 void 类型

// 设置信号函数
void addsig(int sig, void(handler)(int), bool restart = true)
{// 创建 sigaction 结构体变量struct sigaction sa;// 起始地址, 初值,字节数memeset(&sa, '\0', sizeof(sa));// 信号处理函数中仅发送 信号值,不做对应逻辑处理sa.sa_handler = handler; // 函数指针// 对结构体变量 按位或,SA_RESTART 标志位变1// 表示需要自动重启if (restart)sa.sa_flags |= SA_RESTART;// 所有信号添加到 信号集 中sigfillset(&sa.sa_mask);// 执行 sigaction() 函数assert(sigaction(sig, &sa, NULL) != -1);
}

项目中设置信号函数,仅关注 SIGTERM 和 SIGALRM 两个信号

信号通知逻辑

  • 创建管道,管道写端 写入信号值,管道读端 通过 I / O 复用系统 检测读事件
  • 设置信号处理函数 SIGALRM(时间到了触发)和 SIGTERM(kill 会触发,ctrl + c)
    • 通过 struct sigaction 结构体 和 sigaction() 函数,注册信号捕捉函数(结构体和函数进行关联)
    • 在结构体的 handler 参数,设置信号处理函数,具体的,从 管道写端 写入信号名字
  • 利用 I /O 复用系统,监听 管道读端 文件描述符的可读事件
  • 信息值 传递给主循环,主循环再根据接收到的 信号值,执行目标信号对应的逻辑代码

代码

// 创建管道套接字
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
assert(ret != -1);// 设置管道写端非阻塞,为什么写端要非阻塞?
setnonblocking(pipefd[1]);// 设置管道读端为 ET 非阻塞
addfd(epollfd, pipefd[0], false);// 传递给主循环的信号值,这里只关注 SIGALRM 和 SIGTERM
addsig(SIGALRM, sig_handler, false);
addsig(SIGTERM, sig_handler, false);// 循环条件
bool stop__server = false;// 超时标志
bool timeout = false;// 每隔 TIMESLOT 时间,触发 SIGALRM 信号
alarm(TIMESLOT);while (!stop_server)
{// 监测发生事件的文件描述符int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);if (number < 0 && errno != EINTR)break;// 轮询文件描述符for (int i = 0; i < number; i++) {int sockfd = events[i].data.fd;// 管道读端 对应的 文件描述符 发生读事件/*按位与 & 运算符判断某个特定的标志位是否在 events[i].events 中被设置(是否发生了读事件)如果结果为真,表示发生了读事件程序将会执行相应的逻辑来处理该事件*/if ( (sockfd == pipefd[0]) &&  (events[i].events & EPOLLIN) ){int sig;char signals[1024];// 从 管道读端 读出信号值,成功-返回字节数,失败-返回-1// 一般,这里的ret返回值总是 1// 只有 14,15 两个 ASCII码 对应的字符ret = recv(pipefd[0], signals, sizeof(signals), 0);if (ret == -1) // handle the errorcontinue;else if (ret == 0)continue;else {// 处理 信号值 对应的逻辑for (int i = 0; i < ret; ++i) {switch (signals[i]) { // charcase SIGALRM: // int{timeout = true;break;}case SIGTERM:stop_server = true;}}}}}
}

补充解释

ret = recv(pipefd[0], signals, sizeof(signals), 0);
  • pipefd[0] 是管道的读端文件描述符
  • signals 是一个缓冲区,用于存储接收到的数据
  • sizeof(signals) 表示 signals 缓冲区的大小,即要接收的数据的最大字节数
  • 0 是可选的参数,用于指定接收数据时的额外标志。在此处,它表示没有任何特殊的处理要求

函数执行过程👇

  1. recv() 函数阻塞等待,直到管道读端文件描述符 (pipefd[0]) 中有数据可读
  2. 一旦有数据可读,recv() 函数将读取数据并将其存储在 signals 缓冲区中
  3. recv() 函数返回读取的字节数,并将其赋值给变量 ret,以便后续处理

问题1:为什么 管道写端 要 非阻塞?

send() 将信息发送给套接字缓冲区,如果缓冲区满了,就会阻塞

这时会进一步增加 信号处理函数 的执行时间,为此,将其修改为 非阻塞

👆补充解释

  • 代码中的 setnonblocking(pipefd[1]) 调用将管道写端设置为非阻塞模式,这意味着写入管道时不会被阻塞,而是立即返回。这是因为在主循环中,当有事件发生时,程序会将相应的数据写入管道以触发 SIGALRM 或 SIGTERM 信号,从而实现定时和停止服务器的功能。如果管道写端是阻塞的,则当数据无法立即写入管道时,程序会被阻塞等待,直到数据写入成功或出现错误。这可能会导致定时事件失效或无法及时停止服务器。因此,将管道写端设置为非阻塞模式必要的 

问题2:没有对 非阻塞返回值 处理,如果 阻塞 是不是意味着这一次 定时事件 失效 了?

对,但 定时事件 不是必须立即处理的事件,可以允许这样的情况发生

👆补充解释

  • 代码中的管道写端是非阻塞的,因此如果写入的数据无法立即发送,则会立即返回,并且不会阻塞等待。如果没有处理非阻塞返回值,则会导致写入失败而没有得到及时处理。如果这种情况经常发生,则可能会导致定时事件失效,因为无法在规定的时间内将数据写入管道
  • 定时事件不是必须立即处理的事件。在这个例子中,定时事件是通过 SIGALRM 信号实现的,每隔 TIMESLOT 时间就会触发一次该信号。即使写入管道失败,也不会导致 SIGALRM 信号失效,因为下一次定时事件仍然会在规定的时间内触发。因此,在这种情况下,可以允许写入管道失败并且不处理非阻塞返回值

问题3管道 传递的是什么类型?switch-case 的变量冲突?

信号 本身是 整型,管道中传递的是 ASCII码 表中整型对应的字符

switch 的变量,一般为 字符或整型,当 switch 变量为字符,case 中可以为字符,也可以是字符对应的 ASCII 码

相关文章:

WebServer -- 定时器处理非活动连接(上)

目录 &#x1f34d;函数指针 &#x1f33c;基础知识 &#x1f419;整体概述 &#x1f382;基础API sigaction 结构体 sigaction() sigfillset() SIGALRM, SIGTERM 信号 alarm() socketpair() send() &#x1f4d5;信号通知流程 统一事件源 信号处理机制 &#x…...

微服务部署:金丝雀发布、蓝绿发布和滚动发布的对比

金丝雀发布、蓝绿发布和滚动发布的对比 金丝雀发布、蓝绿发布和滚动发布都是软件发布策略&#xff0c;它们都旨在降低发布风险并提高发布速度。但是&#xff0c;这三种策略在工作方式、优缺点等方面存在一些差异。 工作方式 金丝雀发布&#xff1a;将新版本软件逐步发布给用…...

轻松入门MySQL:优化复杂查询,使用临时表简化数据库查询流程(13)

在进销存管理系统中&#xff0c;复杂的数据查询是司空见惯的。这些查询往往需要处理大量的数据&#xff0c;并执行复杂的逻辑操作。然而&#xff0c;处理这些查询可能会变得非常耗时&#xff0c;并且难以维护。为了解决这个问题&#xff0c;我们可以利用临时表&#xff0c;这是…...

vmware的ubuntu虚拟机因空间满无法启动

正在虚拟机编译android源代码&#xff0c;没注意空间不足&#xff0c;结果回来发现了 Assuming drive cache: write through 的问题&#xff0c;经查是空间不足的原因 按照这个教程&#xff0c;清除出来部分空间&#xff0c;才能进去系统&#xff0c;并且对系统空间做下优化 …...

Unity数据持久化之PlayerPrefs

这里写目录标题 PlayerPrefs概述基本方法PlayerPrefs存储位置实践小项目反射知识补充数据管理类的创建反射存储数据----常用成员反射存储数据----List成员反射存储数据----Dictionary成员反射存储数据----自定义类成员反射读取数据----常用成员反射读取数据----List成员反射读取…...

uniapp微信公众号H5分享

如果项目文件node_modules中没有weixin-js-sdk文件&#xff0c;则直接使用本文章提供的&#xff1b; 如果不生效&#xff0c;则在template.h5.html中引入 <script src"https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> 首先引入weixin-js-…...

深入理解指针(c语言)

目录 一、使用指针访问数组二、数组名的理解1、数组首元素的地址2、整个数组 三、一维数组传参的本质四、冒泡排序五、二级指针六、指针数组 一、使用指针访问数组 可以使用指针来访问数组元素。例如&#xff0c;可以声明一个指针变量并将其指向数组的第一个元素&#xff0c;然…...

高级语言期末2015级唐班B卷

1.编写函数&#xff0c;按照如下公式计算圆周率π的值&#xff08;精确到1e-5&#xff09; #include <stdio.h>double pai() {double last0;double flag1;int n1;while(flag-last>1e-5) {lastflag;flag*1.0*(2*n)*(2*n)/((2*n-1)*(2*n1));n;}return 2*last; }int main…...

开发一款招聘小程序需要具备哪些功能?

随着时代的发展&#xff0c;找工作的方式也在不断变得简单&#xff0c;去劳务市场、人才市场的方式早就已经过时了&#xff0c;现在大多数年轻人都是直接通过手机来找工作。图片 找工作类的平台不但能扩大企业的招聘渠道&#xff0c;还能节省招聘的成本&#xff0c;方便求职者进…...

嵌入式学习-qt-Day3

嵌入式学习-qt-Day3 一、思维导图 二、作业 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳…...

零基础到高级:Android音视频开发技能路径规划

音视频开发趋势 Android音视频开发领域目前正处于一个高速发展的阶段&#xff0c;主要趋势如下&#xff1a; 超高清视频&#xff1a;4K视频亚毫米级显示清晰&#xff0c;更加逼真&#xff0c;为开发更加逼真的虚拟现实应用提供了基础。AI技术&#xff1a;自适应码率控制、视频…...

阿里云香港轻量应用服务器网络线路cn2?

阿里云香港轻量应用服务器是什么线路&#xff1f;不是cn2。 阿里云香港轻量服务器是cn2吗&#xff1f;香港轻量服务器不是cn2。阿腾云atengyun.com正好有一台阿里云轻量应用服务器&#xff0c;通过mtr traceroute测试了一下&#xff0c;最后一跳是202.97开头的ip&#xff0c;1…...

python中websockets与主线程传递参数

目录 一、子线程创建websockets服务端接收客户端数据 二、主线程内启动子线程接收并处理数据 一、子线程创建websockets服务端接收客户端数据并存入队列 发送的消息客户端与服务端统一&#xff0c;多种消息加入判断的标签 服务端&#xff1a;web_server.py import asynci…...

js谐音梗创意小游戏《望子成龙》

&#x1f33b; 前言 龙年到来&#xff0c;祥瑞满天。愿您如龙般矫健&#xff0c;事业腾飞&#xff1b;如龙鳞闪耀&#xff0c;生活美满。祝您龙年大吉&#xff0c;万事如意&#xff01; 龙年伊始&#xff0c;我给各位设计了一款原创的小游戏&#xff0c;话不多说&#xff0c;直…...

第十篇:node处理404和服务器错误

🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录</...

左右互博。

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 讨厌鬼在和小甜妹在玩石头游戏。 游戏一开始有 nnn 堆石子&#xff0c;第 iii 堆石子&#xff0c;有 aia_iai​ 个石子。两人轮流进行游戏。 轮到某个人时&#xff0c;这个人先选数量为 x(x&…...

android通过广播打印ram使用信息

在内存非常吃紧的情况下&#xff0c;android设备会开始kill部分非系统进程甚至系统进程来保证基本的系统运行。在这种情况下如何获取设备过去某段时间的ram使用情况至关重要。 通过开发者模式中的“内存”可以完美得知设备内存使用信息。 我们可以通过此途径&#xff0c;设计一…...

内存管理——线性内存,进程空间

低2G为进程空间 开始地址结束地址大小属性00xFFFFF1M保留0x1000000x102FFF栈不固定位置、大小0x1030000x143FFF堆不固定位置、大小0x400000主程序文件不固定位置、大小加载dll不固定位置、大小0x7ffdd000TIB位置&#xff0c;大小编译时固定0x7FFFE000系统与用户共享数据块位置…...

入门Python必读的流程控制语句

流程控制 if-else 语法: if 条件:语句else:语句 例子: a1 #使用方式一 if a>1:print(大于1) else:print(小于等于1) #使用方式二 print(大于1) if a>1 else print(小于等于1) 输出: >>小于等于1 >>小于等于1 if-elif-else 语法: if 条件:语句elif 条件:…...

day05-进程通信

1> 将互斥机制的代码实现重新敲一遍 代码&#xff1a; #include<myhead.h>int num520;//临界资源//1.创建互斥锁 pthread_mutex_t fastmutex;//定义任务函数 void *task1(void *arg){printf("1111111\n");//3.临界区上面获取锁资源&#xff08;上锁&#…...

如何将OpenAI Sora生成的普通AI视频转化为Vision Pro的空间视频,沉浸式体验

【基于AI的Vision Pro空间视频】工作流:这个工作流程用于将2D视频转换为适用于 Vision Pro的Spatial视频: 1、使用Deep3D将2D视频转换为3D SBS: 使用Deep3D工具将2D视频转换为3D SBS格式: 转换例子:Prediction– lucataco/deep3d – Replicatehttps://replicate.com/…...

爬虫基础(下)

requests模块可以用来获取网络数据&#xff1b; 那么对于爬虫来说&#xff0c;要获取下图网页中的内容&#xff0c;就需要网页的URL。 复制链接方法是&#xff0c;打开网页&#xff0c;点击链接框&#xff0c;右键选择复制。 requests.get()函数可用于模拟浏览器请求网页的过…...

【八股文面试】Java基础常见面试题总结(上)

Java基础常见面试题总结(上) Java有哪些特性 简单易学&#xff1b;面向对象&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;&#xff1b;平台无关性&#xff08; Java 虚拟机实现平台无关性&#xff09;&#xff1b;支持多线程&#xff08; C 语言没有内置的多…...

c++:蓝桥杯的基础算法2(构造,模拟)+练习巩固

目录 构造 构造的基础概念&#xff1a; 模拟 练习1&#xff1a;扫雷 练习2&#xff1a;灌溉 练习3&#xff1a;回文日期 构造 构造的基础概念&#xff1a; 构造算法是一种用于解决特定问题的算法设计方法。在C语言中&#xff0c;构造算法通常涉及到创建一个函数或类来实…...

C++ 和 C#的区别

如是我闻&#xff1a; C#&#xff08;发音为 “C sharp”&#xff09;和C是两种流行的编程语言&#xff0c;它们各有特点和用途。下面是这两种语言的一些主要区别&#xff1a; 设计理念和用途: C: 是一种多范式编程语言&#xff0c;支持过程化编程、面向对象编程、泛型编程等。…...

2.14日学习打卡----初学Zookeeper(一)

2.14日学习打卡 目录: 2.14日学习打卡Zookeeper概念一. 集中式到分布式单机架构集群架构什么是分布式三者区别 二. CAP定理分区容错性一致性可用性一致性和可用性的矛盾一致性和可用性如何选择 三. 什么是Zookeeper分布式架构Zookeeper从何而来Zookeeper介绍 四. 应用场景数据发…...

SkyWalking之APM无侵入可观测原理分析

一、 简介&#xff08;为什么需要用到可观测能力&#xff09; 随着微服务的开发模式的兴起&#xff0c;早期的单体架构系统已拆分为很多的子系统&#xff0c;各个子系统封装为微服务&#xff0c;各服务间通过HTTP协议RESET API或者RPC协议进行调用。 在单体服务或者微服务较少的…...

Missing artifact org.yaml:snakeyaml:jar:1.29

关于导入本地maven项目pom.xml出现missing artifact org....报错处理 环境变量配置maven&#xff0c;eclipse中配置maven&#xff0c;重启eclipse。...

三opencv源码解压及环境变量配置

1.双击opencv-3.4.6-vc14-vc15.exe 2.选择解压的路径&#xff0c;点击【extract】 3.设计环境变量...

vue实时监控视频播放的进度,并在播放80%位置触发相应操作

video标签&#xff1a;播放时触发canplay事件 <video:src"filePath"controlsv-if"filePrefix mp4 || filePrefix avi"canplay"getVideoDur()"id"myVideo"class"preview"></video>canplay触发的方法&#xff…...