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

Reactor

文章目录

    • 正确的理解发送
    • double free问题
  • 1.把我们的reactor进行拆分
  • 2.链接管理
  • 3.Reactor的理论

listensock只需要设置_recv_cb,而其他sock,读,写,异常
所以今天写nullptr其实就不太对,添加为空就没办法去响应事件
在这里插入图片描述
获取新链接并没有对它做任何处理,那么我要对他做处理

Accepter 连接管理器
主要负责处理进行处理所有连接的

还有一批接口是事件管理器
也就是统一进行对所有事件来进行响应

获取了新链接,然后AddConnection把它设置进去 ,
在这里插入图片描述之后已经把对应的新链接添加到内核里了,所以未来再进行事件循环时,一定会帮我们检测到已经就绪的事件,而这个时候就可能会包含普通的事件就绪,
在这里插入图片描述
普通的事件就绪他就会检测是否是读事件还是写事件,连接是否是健康的,然后直接执行曾经绑定的方法,对于普通的文件描述符,他绑定的方法刚好是它曾经设置的回调,也就是刚刚绑定的Recver
只要执行Recver,最终再把自己的connection对象指针传进去,所以未来在Recver这通过connection里面的fd , 此时直接读。

接下来的工作就是处理读了
你知道怎么处理数据吗?
你知道你要把数据处理的时候,从connection里面怎么读吗,我们调recv就可以读,我们曾经添加这个fd已经设置为非阻塞了,
在这里插入图片描述
而且他是ET的,根据上节课所讲,ET模式下一旦事件就绪我们只能疯狂的把数据全部读上来,可是数据里面有什么你自己知道吗?
对于tcpserver来讲它在读的时候,应不应该关心数据的格式???
同学们说应该,应该关心数据从哪到哪是个报文,从哪到哪是个什么东西。

答案是 不应该
你是个啥?你是个服务器,

在这里插入图片描述

你只需要关心IO数据就可以,至于这个数据有没有读完,报文的格式细节,这些你不用管。
你只需要帮我把数据全部收上来就可以 了
这才是比较关键的。

我们要处理一个业务,一定是先把数据收上来,收上来之后交给上层去处理,你别越俎代庖的把别人的事情做了,软件分层很重要。

所以接下来Recver只需要读就可以了
connection是形参传进来的,里面不是有fd吗。
我把fd拿进来
那读到哪里呢?
因为是ET模式必须得一直读,循环读,必须把本轮数据全读完,读取过程中根本不用关心读到的数据的格式
所以定义一个基本大小 g_buffer_size = 128
设置char buffer[ g_buffer_size ] 缓冲区
我们用recv 把数据读取到这个buffer中,而且标志位设置为0,代表阻塞读取,可是我们早把fd 设置为非阻塞了,所以这个recv 读取的时候还是非阻塞读取
在这里插入图片描述

recv 返回值
大于 0 读取成功
读取成功该怎么办?你知不知道对应的报文读完了没?你不知道,所以你要一直读,读到他出错。
我们在connection中设置 函数 向connection的string inbuffer直接拼接新内容就可以
在这里插入图片描述
在这里插入图片描述

等于 0 对端把链接关闭了
那我服务器就直接就不用处理这个链接了,
一旦读出现错误,我就直接让连接进入到异常处理回调
在这里插入图片描述

小于 0 读取出错
出错情况一 真的出错
在这里插入图片描述

真的出错,也进入到异常处理,所以所有的读和写所有的异常,全部在Excepter处理就可以了在这里插入图片描述
这也就是为什么当前多路转接这里检测它如果有错误事件继续,异常事件继续,把他的事件全部设成读写事件继续,因为我们想把未来整个服务器所有的异常处理全部都放在这一个Excepter处理

出错情况二
非阻塞一直循环读取的时候,如果出错了,但是errno 错误码 如果等于 EWOULDBOLCK,说明这次读,一直读 读出错了,表明把本轮数据全读完了,读完了我们就不处理了,所以直接break就可以了

另外读取期间,如果 errno 错误码 等于 EINTR 表示读的时候被异常信号中断了,所以continue让他继续去读取。

其他情况才是真正的出错了,也就是第一种情况,这里用else完事。
在这里插入图片描述
所以可以看到connection里的接受缓冲区数据越来越多了。
在这里插入图片描述
读到了数据,可是读完了之后对我来讲,我怎么知道数据 要不要让上层去处理,你不是已经把一批数据全读上来了,此时我怎么知道这个数据在上层应该怎么处理呢?

处理应该交给上层做,你怎么交给上层呢?

服务器已经处理完了IO问题,我们给TcpServer再设置一个 让上层处理信息的回调OnMessage
在这里插入图片描述

把本轮数据全读完,到底这个数据能不能处理 读完之后交给上层,交给上层的时候要把connection对象交给这个回调,因为你读到的sock的所有的数据在connection结构体内部接受缓冲区里。
在这里插入图片描述

直接读完之后回调给上层
在代码中初始化的时候,构造tcpserver时,就需要给我再传个回调
在这里插入图片描述
对于tcpserver来讲OnMessage他是个回调,所有上层就要帮我们处理了,处理的时候一旦读成功了把数据本轮读完了,OnMessage就应该帮我处理,所以OnMessage上层要做的核心工作是:数据有了,但是不一定全,所以要求上层,1.检测 2.如果有完整报文,就处理

所以上层你怎么知道报文是一个完整报文呢?你怎么知道报文完整就要处理,怎么处理呢?
再往上写,就到了之前对特定缓冲区我们根据协议先把报文分包,如果能分出来就分,分不出来就不管,分出来了再反序列化再处理。

那这个回调是谁呢?
这里用输出这个connection缓冲区的数据打印出来测试
在这里插入图片描述
在这里插入图片描述

在往上就到了把报文分割,在做反序列化,就要有协议了,要有应用层了。

我们把之前网络版本计算器的协议相关头文件拿过来

TcpServer.hpp 服务器处理IO 的
Calculator .hpp 处理业务的
两个之间如何耦合呢?
在这里插入图片描述
Handler会将读上来的报文解码反序列化然后计算再序列化编码最后返回响应字符串结果
,接下来要把字符串结果发送出去。
得到计算结果之后要发送出去就和Calculator没关系了,就和服务器有关系,所以一会再绕进入服务器内部再进行处理。

我们的业务逻辑比较简单,没有特别耗时的操作
如果有耗时的操作,我们可以把读到的报文,解码,反序列化,处理计算工作就不做了,可以把之前的线程池拿进来,这里Handler的时候把所有的任务推送 到线程池里,让线程池处理,处理完之后在由线程池把处理的结果写给tcpserver让它再来发。

如果Handler处理结果字符串是空的,那我们就直接返回,让底层继续读,再事件就绪他会继续向inbuffer里追加,
最后一旦有完整的请求了,此时响应就处理完了,它也把缓冲区的字符串该移走就移走了。

下面就要开始发送了
该怎么发送呢?
你想发,前提条件是你要把对应的数据添加到发送缓冲区。
现在又提拱了AppendOutbuffer函数
在这里插入图片描述
我们要发怎么做呢?
我们在连接管理器里只设置了对fd的读关心,我们并没有关心它的写事件,那怎么处理写呢?

在这里插入图片描述

把计算的响应结果字符串添加到connection发送缓冲区里。
在这里插入图片描述

正确的理解发送

1、我们要把数据真正发出去,在epoll/select/poll中,因为写事件表明的本质是发送缓冲区是否有空间,而这个经常是有空间的,所以对于写事件而言,所以经常就是就绪的,
2、如果我们设置对EPOLLOUT事件关心,而EPOLLOUT几乎每次都会就绪,它会导致epoll经常返回,可我们真正关心的是有没有数据发,所以他会浪费CPU 的资源

结论
在写多路转接的代码时
对于读,设置常关心。
对于写,我们是按需设置。

什么是按需设置?
代码体现

3、那怎么处理写呢?
不用考虑epoll,直接写入,如果写入完成,就结束。
如果写入完成,但是数据没有写完,outbuffer里还有内容,我们就需要设置对写事件进行关心了,如果写完了,去掉对写事件的关心!

我们现在上层已经把数据放到outbuffer里,怎么发呢?
在这里插入图片描述

我们直接调用connection里面的Sender回调

首先走到Sender 的时候一定是上层已经在connect里面的outbuffer里面写了数据,当然你不写也行,我会做检测。

因为是ET模式的,非阻塞的,所以接下来while循环

我们直接send发就行了
在这里插入图片描述

send返回值是实际写入的个数
你上层在发的时候,TCP发送缓冲区只剩512字节,可你outbuffer里面可能是1024个字节,所以你期望全发,可实际只能发返回值个。
也就是返回值 > 0
如果返回值大于0,说明他发送成功了,他到底发了多少数据我们也不清楚,接下来发送成功我们要做的就是 从outbuffer里把发送成功的字符移除掉,移除掉返回值个,也就是n个
移除之后要判断,如果outbuffer已经是空了,下次就不发了直接break

返回值 == 0
outbuffer可能因为一些原因没数据,他没发,我们就返回就可以了不处理。

返回值 < 0

发送出错了
意味着当前我们要判断了,因为是一直在发,如果errno == EWOULDBLCOK
说明我一直发,可是最后底层发送缓冲区空间不够了,我上层可能还有数据,但底层不够了,不够了我就不能再发了,所以我就break。

另外errno == EINTR被信号中断了,我就continue

除此之外,发送时候就发出错了,那就打印调试信息,进入异常处理然后return不能让它往后走了

在这里插入图片描述
至此在上层就直接进行发送就行了。
在这里插入图片描述

现在while循环一直发,出来的时候有可能因为发的时候数据没发完,但是已经不能再发了,缓冲区满了。
也有可能outbuffer最后已经整体发完了。
走到异常处理我们不管了
所以接下来该怎么办呢?
发完有两种情况,要么把数据情况了,要么数据还没发完。
没法玩要做判断。
如果outbuffer数据没发完,我已经经过了不断的循环 给他发了很多,可是数据没有发完,此时开启对写事件的关心。
对写事件的关心,一旦底层的写缓冲区有空间了他会就绪,就绪他会继续回调Sender继续发(这里的回调是通过这里来回调的,
在这里插入图片描述

发完之后再去检测。

如果outbuffer为空,说明此次发完了,此时关闭对写事件的关心。

那该怎么开启对写事件的关心呢?

在这里插入图片描述
直接把fd 及其 关心的写事件 设置进epoll模型,利用EPOLL_CTL_MOD

这里把读也设置为true了,也就是也对读设置了关心,为什么?
一定要把EPOLLIN带上,要不它可能认为你不再关心EPOLLIN了,虽然确实是以追加的方式加的,但是这样设置的话确定性会高一些。
其实也证明EPOLLIN是常设的。

一个文件描述符,对应一对接受发送缓冲区,读写都是往一个fd读写。

最终上层就可以通过EnableEvent来开启对读写的关心

上层读取数据后获取响应字符串,之后直接调用Sender,这次发有没有可能没发完呢?
在这里插入图片描述

完全有可能,如果没发完,我们就在Sender开启对写事件关心,
在这里插入图片描述
一旦缓冲区有空间了,底层自动会帮我们做事件派发
在这里插入图片描述
其中就会响应到Sender,而Sender会继续再发,如果继续发还是没发完,继续打开对写事件关心,继续去写,如果此时他发完了,那么他会把写事件关心关闭。

错误更正
这里connection里面只能调用_send_cb
在这里插入图片描述

而不是上文中的Sender
可是发送只能由TcpServer来发,你直接拿connection的_send_cb发,这个方法不应该是你调的。

所以我们可以换种写法,你的connection里面包含了一个TcpServer的回指指针,回指指针可以调用类内的Sender方法。
所以直接调的其实是TcpServer内部的方法。
在这里插入图片描述
用我们之前网络版本计算器客户端来发10次请求测试
在这里插入图片描述
说明服务器是给了响应的,所以我们已经走了一圈了,可是我们最终还要处理异常处理问题。
因为右边是连接退出的时候打印的异常

现在我们到异常处理了
在这里插入图片描述

所有问题都转化成了读写的就绪,读写就绪我们也处理了连接的安全,安全的才进行后续的回调

在这里插入图片描述

现在到了异常这里呢,我认为他一定是出问题了,到底是读出问题了还是写出问题了还是其他的已经不重要了,为什么不重要了呢?
因为我要关掉连接了。

所以接下来该怎么做呢?
首先

  1. 你要在epoll中移除对事件的关心。
  2. 关闭异常的文件描述符
  3. 从unordered_map中移除

在这里插入图片描述

那还要不要把new出来的connection对象再给它 delete呢?
在这里插入图片描述

不用了,因为智能指针自动释放空间。

在这里插入图片描述

比如读出问题了,一旦进入异常处理 就不能再让他往后走了,一旦异常处理以后直接return

double free问题

在这里插入图片描述
因为tcpserver里的unordered_map里的智能指针指向了Connection
而 Connection里面的回指指针又指向了tcpserver。

把connection里面的shared_ptr 改为 weak_ptr就可以了

1.把我们的reactor进行拆分

我们可以把listen模块单独拆出来,然后把
listener关心的事件,就当成一个正常的连接添加进去就可以了。

在这里插入图片描述
有什么好处?
意味着往后如果想把服务设置成多线程
有一种最简单的方式
我们创建一个主线程,主线程内部包含一个TcpServer 这其实就是一个Reactor

未来这是你创建的base_server 主server,它只负责进行获取新链接。
未来把他启动之后呢,后面我们再创建多线程,创建多线程时他要做的事情是Loop

然后线程要执行的函数Loop
每一个线程内部继续创建服务器server,至此就相当于每一个线程都会有一个reactor
在这里插入图片描述
在这里插入图片描述

然后呢由主reactor 它往后获得的所有的连接不用再代码当中让他AddConnection
在这里插入图片描述
那么你做什么工作呢,你在tcpserver里维护一个vector fds
在这里插入图片描述

意思就是说,我在主reactor里我自己想办法获取新链接时,想办法维护一个数组
这个主reactor线程只负责把所有连接放上来,获取到的新fd放到vector里就可以了,主线程可以和其他线程通信,还记得之前写的阻塞队列吗,所以我们可以把vector推送给负载均衡式的交给其他reactor
其他reactor做什么呢?他不绑定listen套接字,其他reactor只需要把所有的文件描述符拿进来在他里面做AddConnection这个工作
在这里插入图片描述

也就是说每一个reactor呢,他都把fd获取上来
然后添加到自己对应的reactor里,所以对于fd生命周期的管理由上面的线程管理,而你的主reactor他只负责获取添加到vector里然后让所有线程读到就可以。

那其他线程怎么读到呢?

在这里插入图片描述

你可以设置阻塞队列,得到了多个连接把vector里 的文件描述符push往里面写,最后通知消费线程来消费,然后其他线程读到之后由其他线程处理

这种方案叫做 == 一个线程一个reactor==
一般叫做 one thread one loop

记住一定要把listen套接字拆出来
listener获取连接的时候,会回指tcp_server。
我们不要调用什么AddConection
他这里获取连接时只需要让它把新的fd push到对应的vector当中
在这里插入图片描述
在这里插入图片描述
push到对应的vector当中呢,再来进行操作

今天TcpServer不应该这么叫,他应该叫做事件循环 EventLoop
它最核心的工作就是它
在这里插入图片描述

2.链接管理

优秀的服务器必须得对连接进行管理
直接在connecion里添加
time_T last_active_time字段
获取当前时间戳
在这里插入图片描述
在这里插入图片描述
构造的时候就获取当前时间

把时间戳维护起来
然后每一次读事件就绪,我们直接更新last_active_time 更新当前时间
在这里插入图片描述
如果还有写,继续更新
在这里插入图片描述
如果不活跃的连接肯定是不会更新时间的,我们还有Loop
我们可以每一次有事件继续把epoll等待策略设置成每隔1S timeout一次
所以这里就可以让它do_other_thing()

做其他事情,每次进行循环,循环完了我们do_other_thing要遍历unordered_map,检测对应的connection的最近时间,如果哪一个连接长时间不动了,我怎么知道它不动了呢?
因为每一次connection时间都会做更新,你可以设置一个最大的超时时间,如果当前对应的connection他所指向的last_active_time 和 设定的未来超时时间timeout
比较一下 连接时间戳 小于 timeout
说明当前连接还没有超时
但如果 大于了 说明 超时了
那直接让连接进入异常处理,他会自动关闭连接。

还可以添加定时器的功能。
在这里插入图片描述

class Timer
定时器应该有什么呢?
unint64_t expired_time 未来过期时间
bool type 意味着定时器是否永久存在
func_t cb 回调

未来要有TimeManager
我们用优先级队列,把Timer放进来
将来会存在很多定时器,所有定时器都由堆管理,未来根据超时时间构建最小堆

服务器的属性里可以 添加TimeManager放到类里面
在这里插入图片描述
未来不是会Add添加事件吗,我们可以给每一个连接添加一个定时器
在这里插入图片描述
把他定好,初始化好,未来超时时间一设置,
然后再把定时器放入到最小堆里。
在这里插入图片描述
最终 定时器有了,有了之后呢,定时器是最小堆,从此往后事件循环时 不是有一个do other thing吗,事件派发完,不是有个 do other thing吗,你只需要在循环中检测最小堆 拿top()首部元素 检测是否超时
两种情况
没有超时
当前最小堆当中未来的超时时间 我们拿到 expired
int timeout = 未来超时时间expired - 当前时间

未来在派发时,下一次epoll的阻塞等待时间就改为timeout
在这里插入图片描述
所以每一次进行检测时,这里Dispatcher它应该等多长时间呢?
他应该等的按照最小堆当中最小的超时时间来进行等待。

那如果超时了呢?
直接拿到顶部元素top,你的Timer里面本来就有回调方法,直接执行它的回调,然后最小堆再进行pop,重复这个工作,就能把所有超时全处理了。

超时回调我该做什么工作呢?
你可以绑定内部检测特定连接的状态。

你要更优化的话,我还可以给Timer回指
tcpserver 和 connection
在这里插入图片描述
未来在服务器当中,每一个连接都有一个connection对象,每一个connection再定义一个timer对象,接着可以让他们两个互相指向,至少让timer指向对应的connection
在这里插入图片描述
所以一旦timer 超时了,你就可以定很多策略了,可是超时我不光要看超时,我还要看历史上它有没有活跃,此时我可以结合超时的时间,它超时了我要检测它,检测它可是不一定要移除它,我再要来查一下他的connection的最近活跃时间,如果最近很活跃,那么我重新更新他的超时时间,然后再把它重新定义一个定时器再加到最小堆里,此时就完成了对它里面的处理

3.Reactor的理论

Reactor叫做半同步半异步模型

主要原因是Reactor他要自己去select 和 poll 和epoll 他自己要等,他等的过程其实是同步的过程,IO分两步 等 + 拷贝
等的过程是由select 和 poll 和epoll 来做 这是体现的是同步,reactor参与了IO.

那异步体现在哪里呢?
异步体现在他可以直接回调处理
目前我们处理的时候是让单Reactor把所有事情全做了
但未来呢,我们记住,如果我们今天要处理的时候,如果有特别耗时的工作就可以更复杂了,
左边是IO逻辑
在这里插入图片描述

多Reactor可以再搞上一个线程池
在这里插入图片描述

每一个Reactor有数据要处理直接把他push到线程池里,线程池内部一定会有大量的其他线程进行处理任务

在这里插入图片描述
如果我们的业务特别复杂+耗时,比如有MySQL的操作,否则比较简单就没必要这么搞了。

还有一种模式叫做Proactor,纯异步方式 ,不考虑。

reactor 翻译过来 就是 反应堆模式

反应堆到底是什么意思呢?

reactor 相当于 打地鼠
在这里插入图片描述
我们每一个玩游戏的人就相当于是一个对应的多路转接
然后呢我们要检测每一个洞口上有没有对应的地鼠出来,虽然他没出来,但是我知道,一旦他出来了我就要执行我的回调方法来砸他。

游戏的面板相当于对应的reactor
每一个洞相当于我们的connecion
老鼠上来了叫做事件就绪
执行砸方法就叫做执行回调

这种就叫做反应堆

epoll参与了一次等多个fd,这就是同步,后来把数据读上来了你可以不处理,你想处理就处理,今天写的代码其实是算是一种同步的代码,基于非阻塞的同步代码。

你可以把对应收到的请求交到线程池,你就不处理了,只负责IO ( 等 + 拷贝 ) ,不负责处理,处理的事情推送到后端的任务队列里由线程池处理,线程池处理完了再把结果给你写 到对应的线程里,无非就是在服务器中再加一个对应的成员变量,然后由线程池直接去访问,写进去就可以
在这里插入图片描述
所以这里呢就可以体现出异步,当然这个呢比较复杂。

后半程说的所有东西,你要是能懂最好下来弄一弄
如果你不懂没关系,作为一个程序员,如果能 手写epoll server能搞定了,
在这里插入图片描述
基本上多路转接已经不再是问题了,一些服务器设计问题继续reactor

如果再写那就把后半程 reactor v2搞定

从今天开始往后,凡是很多的网络服务器的代码,同学们都能看懂了,包括什么redis用C写的,底层就是单reactor,它的处理用的是reactro的LT模式。

Linux课程我们就讲到这里,虽然课程结束了,但同学们现在的你们的学习并没有结束。

希望同学们持之以恒,一定要花很多时间再去学习。

如果压力比较大,唯一的做法是把基础能力变得非常强

相关文章:

Reactor

文章目录 正确的理解发送double free问题 1.把我们的reactor进行拆分2.链接管理3.Reactor的理论 listensock只需要设置_recv_cb&#xff0c;而其他sock&#xff0c;读&#xff0c;写&#xff0c;异常 所以今天写nullptr其实就不太对&#xff0c;添加为空就没办法去响应事件 获…...

介绍 Html 和 Html 5 的关系与区别

HTML&#xff08;HyperText Markup Language&#xff09;是构建网页的标准标记语言&#xff0c;而 HTML5 是 HTML 的最新版本&#xff0c;包含了一些新的功能、元素、API 和属性。HTML5 相对于早期版本的 HTML&#xff08;比如 HTML4&#xff09;有许多重要的改进和变化。以下是…...

已有 containerd 的情况下部署二进制 docker 共存

文章目录 [toc]学习目的开始学习dockerd启动 containerd准备配置文件启动 containerd 启动 docker准备配置文件启动 docker 环境验证停止 docker 和 containerd 学习目的 使用容器的方式做一些部署的交付&#xff0c;相对方便很多&#xff0c;不需要担心别人的环境缺少需要的依…...

Springboot @Transactional使用时需注意的几个问题

一、事务的隔离级别 在Springboot应用中&#xff0c;如果我们想实现方法一旦执行有异常产生&#xff0c;就触发事务回滚&#xff0c;可以在方法上面添加Transactional注解。如果应用采用mysql数据库&#xff0c;虽然mysql本身也有事务隔离机制&#xff0c;但在Sping数据库的应…...

西游记战力排名、笔记等

文章目录 战力排名对西游记的理解各个版本游戏题材西游记关卡和妖怪 西游记家喻户晓&#xff0c;没有谁不知道吧&#xff0c;无论是电视剧、影视&#xff0c;还是小说&#xff0c;乃至游戏&#xff0c;很多地方都有西游记的身影。 虽然知道&#xff0c;但总不如对三国啊、水浒啊…...

(2024.12)Ubuntu20.04安装ZED-SDK

一.官网地址 ZED SDK 4.2 - Download | Stereolabs 选择适配版本进行下载 二.安装程序 下载完成后&#xff0c;进入文件目录&#xff0c;打开终端&#xff0c;输入&#xff1a; chmod x ZED_SDK_Ubuntu20_cuda11.8_v4.2.2.zstd.run ./ZED_SDK_Ubuntu20_cuda11.8_v4.2.2.zst…...

Pytorch | 从零构建GoogleNet对CIFAR10进行分类

Pytorch | 从零构建GoogleNet对CIFAR10进行分类 CIFAR10数据集GoogleNet网络结构特点网络整体架构应用与影响Inceptionv1到Inceptionv2 GoogleNet结构代码详解结构代码代码详解Inception 类初始化方法前向传播 forward GoogleNet 类初始化方法前向传播 forward 训练过程和测试结…...

蓝桥杯刷题——day9

蓝桥杯刷题——day9 题目一题干解题思路一代码解题思路二代码 题目二题干解题思路代码 题目一 题干 小蓝最近在研究一种浮点数的表示方法&#xff1a;R格式。对于一个大于0的浮点数d&#xff0c;可以用R格式的整数来表示。给定一个转换参数n&#xff0c;将浮点数转换为R格式整…...

ffmpeg翻页转场动效的安装及使用

文章目录 前言一、背景二、选型分析2.1 ffmpeg自带的xfade滤镜2.2 ffmpeg使用GL Transition库2.3 xfade-easing项目三、安装3.1、安装依赖([参考](https://trac.ffmpeg.org/wiki/CompilationGuide/macOS#InstallingdependencieswithHomebrew))3.2、获取ffmpeg源码3.3、融合xf…...

分布式刚度编织,让可穿戴触觉更出色 ——Haptiknit

大家好&#xff01;今天来了解一项非常有趣的科技成果 ——“Haptiknit&#xff1a;用于可穿戴触觉的分布式刚度编织”——《Haptiknit: Distributed stiffness knitting for wearable haptics》发表于《SCIENCE ROBOTICS》。在现代科技发展中&#xff0c;可穿戴触觉设备越来越…...

Elasticsearch:什么是查询语言?

查询语言定义 查询语言包括数据库查询语言 (database query language - DQL)&#xff0c;是一种用于查询和从数据库检索信息的专用计算机语言。它充当用户和数据库之间的接口&#xff0c;使用户能够管理来自数据库管理系统 (database management system - DBMS) 的数据。 最广…...

PyQt介绍

**PyQt 和 PySide (Qt for Python) 简介** **PyQt** 和 **PySide** 是 Python 中用于开发图形用户界面 (GUI) 应用程序的两个主要框架&#xff0c;它们都是基于 Qt 库的绑定。Qt 是一个跨平台的应用程序开发框架&#xff0c;广泛用于创建图形用户界面、应用程序开发以及嵌入式…...

Oracle 数据库函数的用法(一)

Oracle数据库提供了大量的内置函数&#xff0c;可以用于完成各种操作&#xff0c;如字符串操作&#xff0c;数学计算&#xff0c;日期时间处理&#xff0c;条件判断&#xff0c;序列生成&#xff0c;聚合统计等。以下是一些常用的Oracle数据库函数&#xff1a; 一、oracle 使用…...

labelme标签批量转换数据集json_to_dataset

文章目录 labelme标签批量转换数据集json_to_dataset转换原理单张图片转换多张图片批量转换bat脚本循环法 标注图片提取标注图片转单通道 labelme标签批量转换数据集json_to_dataset 转自labelme批量制作数据集教程。 转换原理 在安装了labelme的虚拟环境中有一个labelme_js…...

《QT 5.14.1 搭建 opencv 环境全攻略》

《QT 5.14.1 搭建 opencv 环境全攻略》 一、引言二、准备工作&#xff08;一&#xff09;软件下载&#xff08;二&#xff09;系统环境确认 三、安装 QT 5.14.1&#xff08;一&#xff09;安装包下载与运行&#xff08;二&#xff09;环境变量配置 四、OpenCV 安装与配置&#…...

Sentry日志管理thinkphp8 tp8 sentry9 sentry8 php8.x配置步骤, tp8自定义异常处理类使用方法

tp8的默认使用的就是composer来管理第三方包, 所以直接使用 composer 来安装 sentry9 即可. 同时tp8和tp5的配置方式不太一样, 这里我们直接使用自定义异常类来处理Sentry的异常. 1. 安装 sentry9 包 # 安装 sentry9 包 composer require "tekintian/sentry9-php" …...

MySQL 基础:开启数据库之旅

MySQL 基础&#xff1a;开启数据库之旅 在当今数字化的时代&#xff0c;数据扮演着至关重要的角色&#xff0c;而数据库管理系统则是存储、管理和操作这些数据的强大工具。MySQL 作为一款广受欢迎的开源关系型数据库管理系统&#xff0c;被广泛应用于各类网站、应用程序以及企业…...

OpenTK 中帧缓存的深度解析与应用实践

摘要: 本文深入探讨了 OpenTK 中帧缓存的使用。首先介绍了帧缓存的基本概念与在图形渲染管线中的关键地位,包括其与颜色缓存、深度缓存、模板缓存等各类缓存的关联。接着详细阐述了帧缓存对象(FBO)的创建、绑定与解绑等操作,深入分析了纹理附件、渲染缓冲区附件在 FBO 中的…...

stm32制作CAN适配器5--WinUsb上位机编写

上次我们要stm32制作了一个基于winusb有canfd适配器&#xff0c;今天我们来制作一个上位机程序来进行报文收发。 上位机还是用以前写好的&#xff0c;只是更改下dll文件。 项目链接器&#xff0c;输入&#xff0c;附加依赖项中增加winusb.lib winusb初始化&#xff1a;#incl…...

【时间之外】IT人求职和创业应知【71】-专利费

目录 2025 ICT产业趋势年会召开&#xff0c;2024年度ICT十大新闻重磅揭晓 海纳致远数字科技申请定制化插件驱动的数据分析专利 阿波罗智联取得语音数据的处理方法、装置、设备和存储介质专利 心勿贪&#xff0c;贵知足。 感谢所有打开这个页面的朋友。人生不如意&#xff0…...

springboot vue 会员营销系统

springboot vue 会员营销系统介绍 演示地址&#xff1a; 开源版本&#xff1a;http://8.146.211.120:8083/ 完整版本&#xff1a;http://8.146.211.120:8086/ 移动端 http://8.146.211.120:8087/ 简介 欢迎使用springboot vue会员营销系统。本项目包含会员储值卡、套餐卡、计…...

Kafka快速扫描

Architecture 系统间解耦&#xff0c;异步通信&#xff0c;削峰填谷 Topic 消息主题&#xff0c;用于存储消息 Partition 分区&#xff0c;通过扩大分区&#xff0c;可以提高存储量 Broker 部署Kafka服务的设备 Leader kafka主分区 Follwer kafka从分区 高性能之道&#xff1a…...

scala基础学习(数据类型)-字符串

文章目录 scala中的字符串引号单引号双引号三引号 常用内置函数length 获取字符串长度charAt 字符串元素访问substring 获取字串indexOf 获取字串位置replace 字符串替换toLowerCase,toUpperCase 字符串大小写转换trim 去除首位空白符split 字符串切割以及查看startsWith,endsW…...

网络架构与IP技术:4K/IP演播室制作的关键支撑

随着科技的不断发展&#xff0c;广播电视行业也在不断迭代更新&#xff0c;其中4K/IP演播室技术的应用成了一个引人注目的焦点。4K超高清技术和IP网络技术的结合&#xff0c;不仅提升了节目制作的画质和效果&#xff0c;还为节目制作带来了更高的效率和灵活性。那么4K超高清技术…...

如何优雅的关闭GoWeb服务器

以下内容均为Let’s Go Further内容节选以及作者本人理解。 这里创建了一个后台进程用于捕获关闭信号&#xff0c;在后台进程中&#xff0c;主要内容为&#xff1a; 创建一个缓冲通道 quit使用signal.Notify函数监听并捕获关机信号SIGINT,SIGTERM&#xff0c;在捕获关机信号后…...

Python爬虫(5) --爬取网页视频

文章目录 爬虫爬取视频指定url发送请求UA伪装请求页面 获取想要的数据解析定位定位音视频位置 存放视频完整代码实现总结 爬虫 Python 爬虫是一种自动化工具&#xff0c;用于从互联网上抓取网页数据并提取有用的信息。Python 因其简洁的语法和丰富的库支持&#xff08;如 requ…...

simulink离散传递函数得到差分方程并用C语言实现

一. 创建连续时间的传递函数 G ( s ) s 2 217 s s 2 384 s 8989 G(s) \frac{s^2217s}{s^2384s8989} G(s)s2384s8989s2217s​ 二. 离散连续时间的传递函数G(s) 2.1 在matlab中用c2d函数双线性变换法离散G(s)&#xff0c; 下面是matlab脚本代码 % 创建连续时间传递函数 …...

第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书正式赛题

第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书-A 目录 一、竞赛阶段 二、竞赛任务书内容 &#xff08;一&#xff09;拓扑图 &#xff08;二&#xff09;模块A 基础设施设置与安全加固(200分) &#xff08;三&#xff09;B模块安全事件响应/网络安全数据取证/…...

Redis内存碎片详解

什么是内存碎片? 你可以将内存碎片简单地理解为那些不可用的空闲内存。 举个例子&#xff1a;操作系统为你分配了 32 字节的连续内存空间&#xff0c;而你存储数据实际只需要使用 24 字节内存空间&#xff0c;那这多余出来的 8 字节内存空间如果后续没办法再被分配存储其他数…...

Python球球大作战

系列文章 序号直达链接表白系列1Python制作一个无法拒绝的表白界面2Python满屏飘字表白代码3Python无限弹窗满屏表白代码4Python李峋同款可写字版跳动的爱心5Python流星雨代码6Python漂浮爱心代码7Python爱心光波代码8Python普通的玫瑰花代码9Python炫酷的玫瑰花代码10Python多…...

机器学习(Machine Learning)的安全问题

最近看论文&#xff0c;看到了”对抗样本“的一些内容&#xff0c;然后又研究了一下其背后的东西&#xff0c;发现还有”机器学习的安全“这一问题&#xff0c;然后找了几篇博客看了一下&#xff0c;发现了篇2019年的比较有意思的文章&#xff0c;这里整理一下&#xff0c;方编…...

ROS1安装教程

一、环境准备 操作系统&#xff1a;Ubuntu 20.04 LTS 注&#xff1a;为保证系统干净&#xff0c;本文使用Docker进行 演示&#xff0c;如已安装相应的Ubuntu系统请忽略。 Docker安装Ubuntu系统步骤如下&#xff1a; # 拉取镜像 docker pull ubuntu:20.04# 创建容器 docker ru…...

大腾智能CAD:国产云原生三维设计新选择

在快速发展的工业设计领域&#xff0c;CAD软件已成为不可或缺的核心工具。它通过强大的建模、分析、优化等功能&#xff0c;不仅显著提升了设计效率与精度&#xff0c;还促进了设计思维的创新与拓展&#xff0c;为产品从概念构想到实体制造的全过程提供了强有力的技术支持。然而…...

Docker 入门:如何使用 Docker 容器化 AI 项目(一)

引言 在人工智能&#xff08;AI&#xff09;项目的开发和部署过程中&#xff0c;环境配置和依赖管理往往是开发者遇到的挑战之一。开发者通常需要在不同的机器上运行同样的代码&#xff0c;确保每个人使用的环境一致&#xff0c;才能避免 “在我的机器上可以运行”的尴尬问题。…...

【04-数据库面试】

如何创建一个新的数据库 数据库是现代信息技术中不可或缺的一部分&#xff0c;它为存储、检索和管理数据提供了强大的工具。无论是企业还是个人&#xff0c;都可能需要创建自己的数据库以满足特定的需求。本文将详细介绍创建一个新数据库的步骤&#xff0c;包括规划、选择数据…...

单元测试使用记录

什么是单元测试 简单来说就是对一个类中的方法进行测试&#xff0c;对输出的结果检查判断是否符合预期结果 但是在多年的工作中&#xff0c;从来没有哪个项目中真正系统的用到了单元测试&#xff0c;因此对它还是很陌生的&#xff0c;也就造成更加不会在项目中区使用它。 如何…...

《深入浅出 Servlet:Java Web 开发的基石》(二)

ServletConfig(熟练) ServletConfig对象对应web.xml文件中的<servlet>元素。例如你想获取当前Servlet在web.xml文件中的配置名&#xff0c;那么可以使用servletConfig.getServletName()方法获取&#xff01; 你不能自己去创建ServletConfig对象&#xff0c;Servlet的in…...

Pytorch | 从零构建MobileNet对CIFAR10进行分类

Pytorch | 从零构建MobileNet对CIFAR10进行分类 CIFAR10数据集MobileNet设计理念网络结构技术优势应用领域 MobileNet结构代码详解结构代码代码详解DepthwiseSeparableConv 类初始化方法前向传播 forward 方法 MobileNet 类初始化方法前向传播 forward 方法 训练过程和测试结果…...

冯诺依曼架构与哈佛架构的对比与应用

冯诺依曼架构&#xff08;Von Neumann Architecture&#xff09;&#xff0c;也称为 冯诺依曼模型&#xff0c;是由著名数学家和计算机科学家约翰冯诺依曼&#xff08;John von Neumann&#xff09;在1945年提出的。冯诺依曼架构为现代计算机奠定了基础&#xff0c;几乎所有现代…...

【Java基础面试题032】Java中的字节码是什么?

回答重点 Java字节码是Java编译器将Java源代码编译后生成的 位于Java源代码与JVM执行的执行的机器码之间。 Java字节码由JVM解释或即时编译&#xff08;JIT&#xff09;为机器码执行 扩展知识 Java字节码的关键点 1&#xff09;字节码结构&#xff1a; Java字节码是与平…...

K8s ConfigMap的基础功能介绍

在 Kubernetes 中&#xff0c;ConfigMap 是一种用于管理配置信息的资源对象&#xff0c;它允许你将 配置信息与代码解耦&#xff0c;方便管理和更新应用配置&#xff0c;而无需重新构建镜像或重启服务。 ConfigMap 的功能 存储配置信息&#xff1a; 可以以 键值对 的形式存储配…...

stm32制作CAN适配器4--WinUsb的使用

前面使用STM32G474芯片的USB模块做了一个CANFD程序&#xff0c;当时用的是HID模式&#xff0c;在实际使用时发现HID模块的通讯速率太慢了&#xff0c;只能1ms传输一帧&#xff0c;就会造成有些掉帧现象。 现在就把HID模块改为在Window下同样免驱的WinUsb来实现CANFD数据的传输。…...

深入理解 Java 中的 ArrayList 和 List:泛型与动态数组

深入理解 Java 中的 ArrayList 和 List&#xff1a;泛型与动态数组 在 Java 编程中&#xff0c;ArrayList 和 List 是最常用的集合类之一。它们帮助我们管理动态数据&#xff0c;支持按索引访问、增加、删除元素等操作。尤其在使用泛型时&#xff0c;理解它们之间的关系及应用…...

[react 3种方法] 获取ant组件ref用ts如何定义?

获取ant的轮播图组件, 我用ts如何定义? Strongly Type useRef with ElementRef | Total TypeScript import React, { ElementRef } from react; const lunboRef useRef<ElementRef<typeof Carousel>>(null); <Carousel autoplay ref{lunboRef}> 这样就…...

VS Code Copilot 与 Cursor 对比

选手简介 VS Code Copilot&#xff1a;算是“老牌”编程助手了&#xff0c;虽然Copilot在别的编辑器上也有扩展&#xff0c;不过体验最好的还是VS Code&#xff0c;毕竟都是微软家的所以功能集成更好一些&#xff1b;主要提供的是Complete和Chat能力&#xff0c;也就是代码补全…...

华为IPD流程6大阶段370个流程活动详解_第一阶段:概念阶段 — 81个活动

华为IPD流程涵盖了产品从概念到上市的完整过程,各阶段活动明确且相互衔接。在概念启动阶段,产品经理和项目经理分析可行性,PAC评审后成立PDT。概念阶段则包括产品描述、市场定位、投资期望等内容的确定,同时组建PDT核心组并准备项目环境。团队培训涵盖团队建设、流程、业务…...

Vue3组件封装技巧与心得

摘要&#xff1a; 日常开发中&#xff0c;用Vue组件进行业务拆分&#xff0c;代码解耦是一个很好的选择&#xff1b; 今天就来分享一下我在使用Vue3进行组件封装的一些技巧和心得&#xff0c;希望能够帮助到大家&#xff1b; 1. 组件特性&#xff1a; 在Vue中组件是一个独立的…...

15.初识接口1 C#

这是一个用于实验接口的代码 适合初认识接口的人 【CSDN开头介绍】&#xff08;文心一言AI生成&#xff09; 在C#编程世界中&#xff0c;接口&#xff08;Interface&#xff09;扮演着至关重要的角色&#xff0c;它定义了一组方法&#xff0c;但不提供这些方法的实现。它要求所…...

渗透测试-前端加密分析之RSA加密登录(密钥来源本地)

本文是高级前端加解密与验签实战的第5篇文章&#xff0c;本系列文章实验靶场为Yakit里自带的Vulinbox靶场&#xff0c;本文讲述的是绕过前端RSA加密来爆破登录。 分析 generateKey函数用来生成随机的RSA公私钥 加密的格式如下&#xff1a; {"username":"admin…...

题海拾贝:力扣 86.分隔链表

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》 欢迎点赞&#xff0c;关注&#xff01; 1、题…...