epoll的LT与ET模式以及阻塞和非阻塞

news2024/11/28 11:54:41

文章目录

  • 1、基本概念
  • 2、关于在socket和EPOLL中的阻塞与非阻塞
  • 3、几种IO模型的触发方式
  • 4、代码验证
  • 5、总结

1、基本概念

Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你

Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你

阻塞IO:当你去读一个阻塞的文件描述符时,如果在该文件描述符上没有数据可读,那么它会一直阻塞(通俗一点就是一直卡在调用函数那里),直到有数据可读。当你去写一个阻塞的文件描述符时,如果在该文件描述符上没有空间(通常是缓冲区)可写,那么它会一直阻塞,直到有空间可写。以上的读和写我们统一指在某个文件描述符进行的操作,不单单指真正的读数据,写数据,还包括接收连接accept(),发起连接connect()等操作

非阻塞IO:当你去读写一个非阻塞的文件描述符时,不管可不可以读写,它都会立即返回,返回成功说明读写操作完成了,返回失败会设置相应errno状态码,根据这个errno可以进一步执行其他处理。它不会像阻塞IO那样,卡在那里不动

2、关于在socket和EPOLL中的阻塞与非阻塞

关于socket中的阻塞与非阻塞,首先明白的是,阻塞与非阻塞是文件(文件描述符)的性质,而不是函数的性质

socket通信中用到的三个文件描述符

客户端:

  • connfd:客户端创建socket时候得到的文件描述符。connect使用这个描述符主动发起链接。

服务端:

  • listenfd:创建socket得到的文件描述符,同时bind和listen使用的也是这个文件描述符
  • clientfd:调用accept得到的文件描述符,也就是用于通信的文件描述符

注意:
accept函数并不参与三次握手过程,accept函数将建立好的连接从全连接队列中移除,并返回clientfd,随后客户端和服务店通过connfd和clientfd进行通信。

socket通信中相关的文件描述符是否设置为阻塞模式对下列api造成的影响

1、当connfd被设置为阻塞模式的时候(默认),connect函数会一直阻塞到连接成功或超时出错,超时值需要修改内核参数
(注意:connfd的阻塞与否影响的不仅是connect,还有客户端的read以及write(send、recv等)函数族)

2、当connfd被设置成非阻塞模式,无论连接是否成功,connect都会立刻返回

调用connect建立连接成功,返回0,失败则返回-1并设置对应的errno
例如:

  1. errno为EINPROGRESS:这表示连接仍在进行中,需要进一步等待。非阻塞式connect才会出现
  2. EACCES:拒绝连接,通常是由于权限问题
  3. EADDRINUSE:地址已经在使用中,无法建立连接
  4. ECONNREFUSED:远程主机拒绝连接
  5. ETIMEDOUT:连接超时,远程主机没有在指定的时间内响应
  6. EHOSTUNREACH:无法到达远程主机

3、当listenfd设置成阻塞模式的时候(默认,无需设置),如果连接全连接队列中有需要处理的连接,accet函数会立即返回,否则会一直阻塞下去,直到新的连接到来

(也就是说,listenfd的阻塞与否影响的是accept,而不会影响bind和listen)
4、当listenfd设置成非阻塞的时候,无论连接全连接队列是否有连接,accpet都会立即返回,不会阻塞。如果有连接,则accept返回对应的socket。如果没有连接,accept返回值小于0,并设置对应的errno为EAGAIN或EWOULDBLOCK

5、当connfd或clientfd设置为阻塞模式的时候(默认),send会尝试发数据,如果对端因为TCP窗口太小导致本段无法发送出去,send函数会一直阻塞到对端TCP窗口变大足以发送数据或者超时;recv则相反,如果此时没有数据可获取,recv函数会一直阻塞直到收取到数据或者超时,有的话,读到数据后返回。send和recv函数的超时时间可以分别使用SO_SNDTIMEO和SO_RCTIMEO两个套接字选项来设置

6、当connfd和clientfd设置成非阻塞模式的时候,send和recv函数都会立即返回,send函数即使因为对端TCP窗口太小发送不出去也会立即返回,recv函数如果无数据可收也会立即返回,此时这两个函数的返回值都是-1,错误码都是EAGIN。这种情况下,send和recv函数的返回值有三种情况,分别是大于0,等于0,小于0。总结 如下:

返回值返回值含义
大于0成功发送(send)或收取(recv) n 个字节
0对端关闭连接
-1返回值为-1并且errno为EAGAIN或EWOULDBLOCK:表示写操作暂时不可用,即套接字当前不可写,需要稍后重试。errno为其他值:表示写操作发生错误,具体的错误原因可以通过查看errno的值来确定。

3、几种IO模型的触发方式

考虑服务端
这里我们要探讨epoll()的水平触发(LT)和边缘触发(ET),以及阻塞IO和非阻塞IO对它们的影响
对于监听的socket文件描述符我们用listenfd代替,对于accept()返回的文件描述符(即要读写的文件描述符)用connfd代替

验证内容如下:

  1. 水平触发的非阻塞listenfd
  2. 边缘触发的非阻塞listenfd
  3. 水平触发的阻塞connfd
  4. 水平触发的非阻塞connfd
  5. 边缘触发的阻塞connfd
  6. 边缘触发的非阻塞connfd

以上没有验证阻塞的listenfd,因为最开始将listenfd添加到epoll中,调用epoll_wait()返回后必定是已就绪的连接,设不设置阻塞accept()都会立即返回

4、代码验证

#include <sys/epoll.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// events数组的大小
const int max_epoll_events = 10;
// buffer缓冲区的大小
const int buffer_size = 5;
// listen的第二个参数,全连接队列的大小
const int listen_size = 10;
// LT模式
const int epoll_lt = 0;
// ET模式
const int epoll_et = 1;
// 文件描述符为阻塞
const int block = 0;
// 文件描述符为非阻塞
const int noblock = 1;

// 设置文件描述符为阻塞
void SetNoblock(int fd)
{
    int old_flag = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, old_flag | O_NONBLOCK);
}

// 注册文件描述符到epoll中,并设置其事件为EPOLLIN(可读事件)
void AddfdToEollp(int epollfd, int fd, int epoll_type, int block_type)
{
    struct epoll_event ev;
    ev.data.fd = fd;
    ev.events = EPOLLIN;

    // 如果是ET模式,则设置EPOLL_ET
    if (epoll_type == epoll_et)
    {
        ev.events |= EPOLLET;
    }

    // 是否设置阻塞
    if (block_type == block)
    {
        SetNoblock(fd);
    }

    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);
}

// LT处理流程
void EpollLT(int fd)
{
    char buffer[buffer_size];
    int size = 0;
    bzero(buffer, buffer_size);

    printf("read begin\n");
    if ((size = read(fd, buffer, buffer_size)) > 0)
    {
        printf("收到消息:%s\n", buffer);
    }
    else if (size == 0)
    {
        printf("客户端关闭连接\n");
        close(fd);
    }
}

// 带循环的ET流程
void EpollETLoop(int fd)
{
    char buffer[buffer_size];
    int size = 0;
    bzero(buffer, buffer_size);
    printf("带循环的ET开始读取数据\n");
    while (true)
    {
        bzero(buffer, buffer_size);
        size = read(fd, buffer, buffer_size);
        if (size > 0)
        {
            printf("收到消息:%s", buffer);
        }
        else if (size == 0)
        {
            printf("客户端关闭连接\n");
            close(fd);
            break;
        }
        else
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                printf("循环读取数据结束\n");
                break;
            }
        }
    }
    printf("带循环的ET处理结束\n");
}

// 不带循环的ET流程
void EpollETNoLoop(int fd)
{
    char buffer[buffer_size];
    int size = 0;
    bzero(buffer, buffer_size);
    printf("不带循环的ET开始读取数据\n");
    size = read(fd, buffer, buffer_size);
    if (size > 0)
    {
        printf("收到消息:%s", buffer);
    }
    else if (size == 0)
    {
        printf("客户端关闭连接\n");
        close(fd);
    }
    printf("不带循环的ET处理结束\n");
}



void EpollProcess(int epollfd, struct epoll_event* events, int number, int listenfd, int epoll_type, int block_type)
{
    for(int i = 0; i < number; ++i)
    {
        int fd = events[i].data.fd;
        //监听套接字有事件发生,一般都是新连接到来
        if(fd == listenfd)
        {
            printf("=============================新一轮accept()=============================\n");
            printf("accept()开始\n");

            //休眠3秒,模拟服务器很繁忙,不能立刻处理accept连接
            printf("服务器繁忙...\n");
            sleep(3);
            printf("服务器繁忙结束...\n");
            
            struct sockaddr_in client_addr;
            socklen_t client_addr_len = sizeof(client_addr);
            int connfd = accept(listenfd, (struct sockaddr*)&client_addr, &client_addr_len);
            //int connfd = accept4(listenfd, (struct sockaddr *)&clientaddr, &len, SOCK_NONBLOCK);
            AddfdToEollp(epollfd, connfd, epoll_type, block_type);
            printf("client ip is %s, client port is %d, accept fd is %d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), connfd);
            printf("accept 结束, fd is %d\n", connfd);
        }
        else if(events[i].events & EPOLLIN)
        {
            if(epoll_type == epoll_lt)
            {
                printf("水平触发开始...\n");
                EpollLT(fd);
            }
            else if(epoll_type == epoll_et)
            {
                printf("边缘触发开始...\n");
                //不带循环的水平触发
                EpollETLoop(fd);
                //带循环的水平触发
                EpollETNoLoop(fd);
            }
        }
    }
}


// 创建监听socket
int CreateListenSocket(const char *ip, const int port)
{
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    //int listensock = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
    if (listenfd < 0)
    {
        fprintf(stderr, "socket error, error code is %d\n", errno);
    }

    struct sockaddr_in servaddr;
    bzero(&servaddr, sizeof(servaddr));
    // memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(ip);
    servaddr.sin_port = htons(port);

    // 设置地址复用
    int on = 1;
    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        fprintf(stderr, "SO_REUSEADDR error, error code is %d\n", errno);
        exit(1);
    }

    // 绑定
    if (bind(listenfd, (struct sockaddr *)&(servaddr), sizeof(servaddr)) == -1)
    {
        fprintf(stderr, "bind error, error code is %d\n", errno);
        exit(2);
    }

    if (listen(listenfd, 5) == -1)
    {
        fprintf(stderr, "listen error, error code is %d\n", errno);
        exit(3);
    }
    return listenfd;
}

int main(int argc, char *argv[])
{
    if (argc < 3)
    {
        fprintf(stderr, "usage:%s ip_address port_number\n", argv[0]);
        exit(4);
    }
    int listenfd, epollfd, number;
    listenfd = CreateListenSocket(argv[1], atoi(argv[2]));

    if ((epollfd = epoll_create1(0)) == -1)
    {
        fprintf(stderr, "epoll_create1 error, error code is%d", errno);
    }

    struct epoll_event events[max_epoll_events];

    //listenfd:非阻塞的LT
    //AddfdToEollp(epollfd,listenfd, epoll_lt, noblock);
    //listenfd:非阻塞的ET
    AddfdToEollp(epollfd,listenfd, epoll_et, noblock);

    while(true)
    {
        number = epoll_wait(epollfd, events, max_epoll_events, -1);//时间参数为0表示立即返回,为-1表示无限等待,大于0表示阻塞多少毫秒
        if(number > 0)
        {
            //connfd:阻塞的LT模式
            //EpollProcess(epollfd, events, number, listenfd, epoll_lt, block);
            
            //connfd:非阻塞的LT模式
            EpollProcess(epollfd, events, number, listenfd, epoll_lt, noblock);

            //connfd:阻塞的ET模式
            //EpollProcess(epollfd, events, number, listenfd, epoll_et, block);

            //connfd:非阻塞的ET模式
            //EpollProcess(epollfd, events, number, listenfd, epoll_et, noblock);
        }
    }
    return 0;
}

水平触发的非阻塞listenfd
放开AddfdToEollp(epollfd,listenfd, epoll_lt, noblock); 和 EpollProcess(epollfd, events, number, listenfd, epoll_lt, block); 然后编译运行

在这里插入图片描述

代码里面休眠了3秒,模拟繁忙服务器不能很快处理accept()请求。这里,我们开另一个终端快速用5个连接连到服务器:

在这里插入图片描述

我们再看看服务器的反映,可以看到5个终端连接都处理完成了,返回的新connfd依次为5,6,7,8,9:

在这里插入图片描述

测试完毕,批量kill掉那5个客户端:

for i in {1..5};do kill %$i;done

边缘触发的非阻塞listenfd
放开打开AddfdToEollp(epollfd,listenfd, epoll_et, noblock); 和 EpollProcess(epollfd, events, number, listenfd, epoll_lt, block);
然后编译运行,采用同样的方式,快速创建5个客户端连接。再看服务器的反映,5个客户端只处理了3个。说明高并发时,会出现客户端连接不上的问题:

在这里插入图片描述

后面4个测试等待listenfd都采用水平触发,后面就不重复写了

水平触发的阻塞connfd
放开EpollProcess(epollfd, events, number, listenfd, epoll_lt, noblock);
编译运行,用一个客户端连接,并发送1-9这几个数字:

在这里插入图片描述
再看服务器的反映,可以看到水平触发触发了2次。因为我们代码里面设置的缓冲区是5字节,处理代码一次接收不完,水平触发一直触发,直到数据全部读取完毕:
在这里插入图片描述

水平触发的非阻塞connfd
放开EpollProcess(epollfd, events, number, listenfd, epoll_lt, noblock);
编译运行,用一个客户端连接,并发送一段数据:

在这里插入图片描述
再看服务器的反映,可以看到水平触发触发了2次。跟水平触发的阻塞connfd一模一样
在这里插入图片描述

边缘触发的阻塞connfd
放开EpollProcess(epollfd, events, number, listenfd, epoll_et, block); 和 EpollETLoop(fd);
先测试不带循环的ET模式(即不循环读取数据,跟水平触发读取一样),编译运行后,开启一个客户端连接,并发送1-9这几个数字,再看看服务器的反映,可以看到边缘触发只触发了一次,只读取了5个字节:
在这里插入图片描述

我们继续在刚才的客户端发送一个字符a,告诉epoll_wait(),有新的可读事件发生:

在这里插入图片描述

这个时候,如果继续在刚刚的客户端再发送一个a,客户端这个时候就会读取上次没读完的a加上次的回车符,2个字节,还剩3个字节的缓冲区就可以读取本次的a加本次的回车符共4个字节:

在这里插入图片描述

我们可以看到,阻塞的边缘触发,如果不一次性读取一个事件上的数据,会干扰下一个事件!!!

接下来,我们就一次性读取数据,即带循环的ET模式。注意:我们这里测试的还是边缘触发的阻塞connfd,只是换个读取数据的方式。

放开EpollETLoop(fd);
编译运行,依然用一个客户端连接,发送1-9。看看服务器,可以看到数据全部读取完毕:

在这里插入图片描述

细心的朋友肯定发现了问题,程序没有输出"带循环的ET处理结束",是因为程序一直卡在了read()函数上,因为是阻塞IO,如果没数据可读,它会一直等在那里,直到有数据可读。如果这个时候,用另一个客户端去连接,服务器不能受理这个新的客户端!!!

边缘触发的非阻塞connfd
不带循环的ET测试同上面一样,数据不会读取完。这里我们就只需要测试带循环的ET处理,即正规的边缘触发用法。
放开EpollProcess(epollfd, events, number, listenfd, epoll_et, noblock);
编译运行,用一个客户端连接,并发送1-9。再观测服务器的反映,可以看到数据全部读取完毕,处理函数也退出了,因为非阻塞IO如果没有数据可读时,会立即返回,并设置error,这里我们根据EAGAIN和EWOULDBLOCK来判断数据全部读取完毕了,可以退出循环了:

在这里插入图片描述

这个时候,我们用另一个客户端去连接,服务器依然可以正常接收请求:

在这里插入图片描述

5、总结

  1. 对于监听的 listenfd,一般设置成非阻塞。最好使用水平触发模式,边缘触发模式会导致高并发情况下,有的客户端会连接不上。如果非要使用边缘触发,可以用 while 来循环 accept()。

  2. 对于读写的 connfd,水平触发模式下,阻塞和非阻塞效果都一样,只要是调用了read读取数据,那么就一定能读取到数据。不过还是建议设置为非阻塞。

  3. 对于读写的 connfd,边缘触发模式下,必须使用非阻塞 IO,并要求一次性地完整读写全部数据。

如果需要及时处理所有就绪事件,尤其是在高并发的情况下,可以选择 ET 模式。
如果应用程序的处理逻辑较为复杂,可能会花费较长的时间处理每个事件,或者存在一些短暂的阻塞情况,可以选择 LT 模式。

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

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

相关文章

Flask-蓝图

1、使用步骤&#xff1a; 创建蓝图 blue Blueprint("myblue01", __name__) 使用蓝图装饰视图函数 blue.route(/) def index():return index 将蓝图注册到app中 from appdemo_blueprint import blue app.register_blueprint(blue) 2、以包的形式使用蓝图 <…

Java007——Java注释学习

围绕以下3点介绍&#xff1a; 1、什么是Java注释&#xff1f; 2、Java注释的作用&#xff1f; 3、Java注释长什么样&#xff0c;以及怎么使用Java注释&#xff1f; 一、什么是Java注释&#xff1f; Java注释是在Java程序中用来描述代码的特殊语句。 注释被忽略并且不被编译器…

PyTorch 深度学习 || 专题六:PyTorch 数据的准备

PyTorch 数据的准备 1. 生成数据的准备工作 import torch import torch.utils.data as Data#准备建模数据 x torch.unsqueeze(torch.linspace(-1, 1, 500), dim1) # 生成列向量 y x.pow(3) # yx^3#设置超参数 batch_size 15 # 分块大小 torch.manual_seed(10) # 设置种子点…

javascript基础三十:JavaScript如何实现上拉加载,下拉刷新?

一、前言 下拉刷新和上拉加载这两种交互方式通常出现在移动端中 本质上等同于PC网页中的分页&#xff0c;只是交互形式不同 开源社区也有很多优秀的解决方案&#xff0c;如iscroll、better-scroll、pulltorefresh.js库等等 这些第三方库使用起来非常便捷 我们通过原生的方…

哈工大 大数据 数据库实验(3) 物理数据库设计--索引结构

指导书: HIT邹老师数据库实验三资源-CSDN文库 导入数据库 操作 两个元组的分布规律 1.查询元组的数据分布 SELECT COUNT(*) FROM Foo; SELECT COUNT(*) FROM Foo WHERE id 0; SELECT COUNT(*) FROM Foo WHERE a 0; SELECT COUNT(*) FROM Foo WHERE b 0; SELECT COUNT(*…

c++ 解析html与htmlcxx库

目录 1&#xff0c;htmlcxx Github 版本源码下载 2&#xff0c;htmlcxx Linux 版本源码下载 3&#xff0c;htmlcxx 解析例子 1&#xff0c;htmlcxx Github 版本源码下载 正如在前一篇文章 c CFile 类 里提到的&#xff0c;我想要从指定的 html 文件里提取…

【裸机开发】镜像文件内部信息构成

由于当前使用的是裸板&#xff0c;没有OS&#xff0c;DDR的初始化、文件保存地址都尚未确定&#xff0c;所以我们生成二进制文件以后&#xff0c;是无法直接放到开发板上运行的。 因此&#xff0c;我们一般会在二进制文件之前加一些头部信息&#xff0c;这些头部信息会告诉开发…

xsync集群分发脚本

脚本&#xff1a; #!/bin/bash #1. 判断参数个数 if [ $# -lt 1 ] thenecho Not Enough Arguement!exit; fi #2. 遍历集群所有机器 for host in hadoop02 hadoop03 doecho $host #3. 遍历所有目录&#xff0c;挨个发送for file in $do#4. 判断文件是否存在if [ -e $file ]th…

文本三剑客awk:命令讲解

awk-命令讲解&#xff1a; 一、awk&#xff1a; 1.定义&#xff1a; &#xff08;1&#xff09;在 Linux/UNIX 系统中&#xff0c;awk 是一个功能强大的编辑工具&#xff0c;逐行读取输入文本&#xff0c;默认以空格或tab键作为分隔符作为分隔。并按模式或者条件执行编辑命令…

006-从零搭建微服务-注册中心(二)

写在最前 如果这个项目让你有所收获&#xff0c;记得 Star 关注哦&#xff0c;这对我是非常不错的鼓励与支持。 源码地址&#xff1a;https://gitee.com/csps/mingyue 文档地址&#xff1a;https://gitee.com/csps/mingyue/wikis 核心依赖 需要注册配置中心的服务引入下面 …

步进电机基本原理及驱动方式详解

步进电机基本原理及驱动方式详解 步进电机相关概念基本原理类型和结构转子结构定子结构 励磁方式ATD9800 驱动使用实例 参考文献 步进电机相关概念 步进电机是一种常用于控制和定位应用的电动机&#xff0c;其独特的工作方式使其在许多领域中都具有广泛的应用。步进电机以其 精…

正态总体下常见的抽样分布

1.正态总体下常见的抽样分布 本人博客&#xff1a;总体分布、样本分布、抽样分布的区别 本人博客&#xff1a;三大抽样分布 正态总体下常见的抽样分布意思是&#xff1a;样本来自服从正态分布的总体中&#xff0c;从样本中抽样后得到的分布 1.1 单个正态总体下的抽样分布&…

【高危】GitLab CE/EE 存在存储型XSS漏洞

漏洞描述 GitLab 是一款基于Git的代码托管、版本控制、协作开发平台。 在 GitLab CE/EE 15.11 至 15.11.6 版本以及 16.0 至 16.0.1 版本中&#xff0c;当GitLab导入GitHub仓库时&#xff0c;如果GitHub仓库中包含由用户构造的恶意JavaScript代码的标签颜色&#xff0c;解析这…

为什么说程序员和产品经理一定要学一学PMP

要回答为什么说程序员和产品经理一定要学一学PMP&#xff1f;我们得先看一下PMP包含的学习内容。PMP新版考纲备考参考资料绝大多数涉及IT项目的敏捷管理理念。主要来源于PMI推荐的10本参考书&#xff1a; 《敏捷实践指南&#xff08;Agile Practice Guide&#xff09;》 《项目…

Java匿名内部类、invoke方法、动态代理

一、匿名内部类 匿名内部类一般作为方法的参数&#xff0c;这个方法的形参为接口&#xff0c;而实参为匿名内部类&#xff08;可以理解为接口的对象&#xff09;并且重写了接口中的方法。 例如&#xff1a; 定义了一个接口Star&#xff1a; 类型为Star的引用数据类型作为方法s…

关于docker中Nacos启动成功访问不了的情况

书接上回&#xff0c;这段时间在忙学成在线的微服务项目&#xff0c;上次出现了IP修改的问题&#xff0c;有了一定的解决方案&#xff0c;复制别人的虚拟机后如何修改IP_SSSEdward的博客-CSDN博客。 这次docker正常启动了&#xff0c;但是出现了访问不了的情况&#xff0c;但是…

短视频矩阵系统源码打包附赠分享

矩阵系统源码主要有三种框架&#xff1a;Spring、Struts和Hibernate。Spring框架是一个全栈式的Java应用程序开发框架&#xff0c;提供了IOC容器、AOP、事务管理等功能。Struts框架是一个MVC架构的Web应用程序框架&#xff0c;用于将数据模型、Web应用程序的用户界面和控制器逻…

【备战秋招】Java异常处理

Java程序在执行过程中所发生的异常事件可分为两类&#xff1a; Error&#xff1a;Java虚拟机无法解决的严重问题。如&#xff1a;JVM系统内部错误、资源 耗尽等严重情况。 比如&#xff1a;StackOverflowError和OOM。一般不编写针对性的代码进行处理。 Exception: 其它因编程错…

使用pytorch进行FFT和STFT

首先&#xff0c;我们定义一个波形&#xff0c;幅值分别为20和38&#xff0c;频率为2和13&#xff1a; y 20 sin ⁡ ( 2 π 2 x ) 38 sin ⁡ ( 2 π 13 x ) y20 \sin (2\pi \times 2x)38\sin (2\pi \times 13x) y20sin(2π2x)38sin(2π13x) 采样频率为200Hz&#xff0c;采…

十四届蓝桥杯省赛CB

hinhin可否不要老&#xff0c;令创新高 A 日期统计B 01 串的熵C 冶炼金属D: 飞机降落E: 接龙数列F: 岛屿个数G: 子串简写H: 整数删除I: 景区导游J: 砍树 A 日期统计 #include <bits/stdc.h> using namespace std; #define int long long int #define pii pair<int,int…