Qt编写推流程序/支持webrtc265/从此不用再转码/打开新世界的大门

news2025/4/21 19:24:11

一、前言

在推流领域,尤其是监控行业,现在主流设备基本上都是265格式的视频流,想要在网页上直接显示监控流,之前的方案是,要么转成hls,要么魔改支持265格式的flv,要么265转成264,如果要追求实时性,那就只有一种方案,就是转码,强制转成264,然后用webrtc显示。当然,如果用户觉得后台修改摄像头配置改成264可以接受,那又是另外一回事了。

为什么webrtc不支持265?据说是因为专利的原因,近些年说是解决了专利问题,现在谷歌浏览器直接内置了265的解码,在新版的浏览器已经支持了265的webrtc,目前谷歌浏览器默认还未开启265,需要手动设置启动参数,官网说未来这个参数默认开启。
如何设置呢?找到桌面快捷方式,右键属性,目标加上 --enable-features=PlatformHEVCEncoderSupport,WebRtcAllowH265Receive,WebRtcAllowH265Send --force-fieldtrials=WebRTC-Video-H26xPacketBuffer/Enabled

完整内容如下 “C:\Program Files\Google\Chrome\Application\chrome.exe” --enable-features=PlatformHEVCEncoderSupport,WebRtcAllowH265Receive,WebRtcAllowH265Send --force-fieldtrials=WebRTC-Video-H26xPacketBuffer/Enabled

如何确定设置后是否真正支持265?打开网页 https://jsfiddle.net/v24s8q1f/ 右下角看到列表中有H265表示成功。

二、效果图

在这里插入图片描述

三、相关地址

  1. 国内站点:https://gitee.com/feiyangqingyun
  2. 国际站点:https://github.com/feiyangqingyun
  3. 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 文件地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_push。

四、功能特点

  1. 支持各种本地音视频文件和网络音视频文件,格式包括mp3、aac、wav、wma、mp4、mkv、rmvb、wmv、mpg、flv、asf等。
  2. 支持各种网络音视频流,网络摄像头,协议包括rtsp、rtmp、http等。
  3. 支持本地摄像头设备推流,可指定分辨率、帧率、格式等。
  4. 支持本地桌面采集推流,可指定屏幕索引、采集区域、起始坐标、帧率等,也支持指定窗口标题进行采集。
  5. 可实时切换预览视频文件,可切换音视频文件播放进度,切换到哪里就推流到哪里。预览过程中可以切换静音状态和暂停推流。
  6. 可指定重新编码推流,任意源头格式可选强转264或265格式。
  7. 可转换分辨率推流,设置等比例缩放或者指定分辨率进行转换。
  8. 推流的清晰度、质量、码率都可调,可以节约网络带宽和拉流端的压力。
  9. 音视频文件自动循环不间断推流。
  10. 音视频流有自动掉线重连机制,重连成功自动继续推流。
  11. 支持各种流媒体服务程序,包括但不限于mediamtx、ZLMediaKit、srs、LiveQing、nginx-rtmp、EasyDarwin、ABLMediaServer。
  12. 通过配置文件自动加载对应流媒体程序的协议和端口,自动生成推流地址和各种协议的拉流地址。可以通过配置文件自己增加流媒体程序。
  13. 可选rtmp、rtmp格式推流,推流成功后,支持多种格式拉流,包括但不限于rtsp、rtmp、hls、flv、ws-flv、webrtc等。
  14. 在软件上推流成功后,可以直接单击网页预览,实时预览推流后拉流的画面,多画面网页展示。
  15. 软件界面上可单击对应按钮,动态添加文件和目录,可手动输入地址。
  16. 推拉流实时性极高,延迟极低,延迟时间大概在100ms左右。
  17. 极低CPU资源占用,4路主码流推流只需要占用0.2%CPU。理论上常规普通PC机器推100路毫无压力,主要性能瓶颈在网络。
  18. 可以推流到外网服务器,然后通过手机、电脑、平板等设备播放对应的视频流。
  19. 每路推流都可以手动指定唯一标识符(方便拉流/用户无需记忆复杂的地址),没有指定则按照策略随机生成hash值。也支持自动按照指定标识后面加数字的方式递增命名。比如设置标识为字母v,策略为标识递增,则每添加一个对应的推流码命名依次是v1、v2、v3等。
  20. 根据推流协议自动转码格式,默认策略按照选择的推流协议,比如rtsp支持265而rtmp不支持,如果是265的文件而选择rtmp推流,则自动转码成264格式再推流。
  21. 音视频同步推流,在拉流和采集的时候就会自动处理好同步,同步后的数据再推流。
  22. 表格中实时显示每一路推流的分辨率和音视频数据状态,灰色表示没有输入流,黑色表示没有输出流,绿色表示原数据推流,红色表示转码后的数据推流。
  23. 自动重连视频源,自动重连流媒体服务器,保证启动后,推流地址和打开地址都实时重连,只要恢复后立即连上继续采集和推流。
  24. 根据不同的流媒体服务器类型,自动生成对应的rtsp、rtmp、hls、flv、ws-flv、webrtc拉流地址,用户可以直接复制该地址到播放器或者网页中预览查看。
  25. 添加的推流地址等信息自动存储到文件,可以手动打开进行修改,默认启动后自动加载历史记录。
  26. 可以指定生成的网页文件保存位置,方便作为网站网页发布,可以直接在浏览器中输入网址进行访问,发布后可以直接在局域网其他设备比如手机或者电脑打开对应网址访问。
  27. 可选是否开机启动、后台运行等。网络推流添加的rtsp地址可勾选是否隐藏地址中的用户信息。
  28. 自带设备推流模块,自动识别本地设备,包括本地的摄像头和桌面,可以手动选择不同的是视频和音频采集设备进行推流。
  29. 自带文件点播模块,添加文件后用户可以拉取地址点播,用户端可以任意切换播放进度。支持各种浏览器(谷歌chromium、微软edge、火狐firefox等)、各种播放器(vlc、mpv、ffplay、potplayer、mpchc等)打开请求。
  30. 文件点播模块实时统计显示每个文件对应的访问数量、总访问数量、不同IP地址访问数量。
  31. 文件点播模块采用纯QTcpSocket通信,不依赖流媒体服务程序,核心源码不到500行,注释详细,功能完整。
  32. 支持任意Qt版本(Qt4、Qt5、Qt6),支持任意系统(windows、linux、macos、android、嵌入式linux等)。

五、相关代码

#include "netpushclient.h"
#include "ffmpegthread.h"
#include "ffmpegsave.h"
#include "videohelper.h"
#include "osdgraph.h"

bool NetPushClient::checkB = false;
bool NetPushClient::recordInteger = false;
int NetPushClient::recordDuration = 0;
int NetPushClient::encodeVideo = 0;
float NetPushClient::encodeVideoRatio = 1;
QString NetPushClient::encodeVideoScale = "1";

NetPushClient::NetPushClient(QObject *parent) : QObject(parent)
{
    ffmpegThread = NULL;
    ffmpegSave = NULL;

    //定时器控制多久录制一个文件
    timerRecord = new QTimer(this);
    timerRecord->setInterval(1000);
    connect(timerRecord, SIGNAL(timeout()), this, SLOT(checkRecord()));
}

NetPushClient::~NetPushClient()
{
    this->stop();
}

QString NetPushClient::getMediaUrl()
{
    return this->mediaUrl;
}

QString NetPushClient::getPushUrl()
{
    return this->pushUrl;
}

FFmpegThread *NetPushClient::getVideoThread()
{
    return this->ffmpegThread;
}

void NetPushClient::checkRecord()
{
    //0. 时长单位分钟/触发条件自动重新录像/recordDuration=0/表示禁用录像
    //1. recordInteger参数控制是否整数倍数录像/recordDuration参数控制录制文件时长/整数倍录像下时长为对应的模数
    //2. 一般监控行业会按照整点录像/比如30分钟60分钟一个视频文件/这样录制的文件起始时间和结束时间整整齐齐
    //3. 整点录像情况下除了第一个和最后一个录像文件可能时长不一样/中间的文件肯定时长都一样
    //4. 非整点录像就按照录像总时长计时/所有保存的文件都是按照时长保存的

    //5. recordInteger=true/recordDuration=5/表示每到5分钟的时候录制一个文件
    //6. 上面录制结果: 11:01开始录制/11:05结束上一个录制并重新录制/第一个文件时长4分钟
    //7. recordInteger=false/recordDuration=5/表示每过5分钟的时候录制一个文件
    //8. 上面录制结果: 11:01开始录制/11:06结束上一个录制并重新录制/第一个文件时长5分钟

    bool ok = false;
    QDateTime now = QDateTime::currentDateTime();
    qint64 offset = recordTime.msecsTo(now);
    if (recordInteger && recordDuration > 1) {
        QTime time = now.time();
        int min = time.minute();
        int sec = time.second();
        min = (min == 0 ? 60 : min);
        ok = ((min % recordDuration == 0) && sec >= 0 && sec <= 2);
        //qDebug() << TIMEMS << min << sec << (min % recordDuration == 0) << offset << ok;
    } else {
        ok = (offset >= (recordDuration * 60 * 1000));
        //qDebug() << TIMEMS << recordDuration << offset << ok;
    }

    if (ok && offset >= 5000) {
        this->record();
    }
}

void NetPushClient::record()
{
    if (ffmpegSave) {
        //取出推流码
        QString flag = pushUrl.split("/").last();
        //文件名不能包含特殊字符/需要替换成固定字母
        QString pattern("[\\\\/:|*?\"<>]|[cC][oO][mM][1-9]|[lL][pP][tT][1-9]|[cC][oO][nM]|[pP][rR][nN]|[aA][uU][xX]|[nN][uU][lL]");
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
        QRegularExpression rx(pattern);
#else
        QRegExp rx(pattern);
#endif
        flag.replace(rx, "X");

        //文件名加上时间结尾
        QString path = QString("%1/video/%2").arg(qApp->applicationDirPath()).arg(QDATE);
        QString name = QString("%1/%2_%3.mp4").arg(path).arg(flag).arg(STRDATETIME);

        //目录不存在则新建
        QDir dir(path);
        if (!dir.exists()) {
            dir.mkpath(path);
        }

        //先停止再打开重新录制
        ffmpegSave->stop();
        ffmpegSave->open(name);
        recordTime = QDateTime::currentDateTime();
    }
}

void NetPushClient::receivePlayStart(int time)
{
    //演示添加OSD后推流
#ifdef betaversion
    int height = ffmpegThread->getVideoHeight();
    QList<OsdInfo> osds = OsdGraph::getTestOsd(height);
    ffmpegThread->setOsdInfo(osds);
#endif

    //打开后才能启动录像
    ffmpegThread->recordStart(pushUrl);

    //推流以外还单独存储
    if (!ffmpegSave && recordDuration > 0) {
        //源头保存没成功就不用继续
        FFmpegSave *saveFile = ffmpegThread->getSaveFile();
        if (!saveFile->getIsOk()) {
            return;
        }

        ffmpegSave = new FFmpegSave(this);
        //重新编码过的则取视频保存类的对象
        AVStream *videoStreamIn = saveFile->getVideoEncode() ? saveFile->getVideoStream() : ffmpegThread->getVideoStream();
        AVStream *audioStreamIn = saveFile->getAudioEncode() ? saveFile->getAudioStream() : ffmpegThread->getAudioStream();
        ffmpegSave->setSavePara(ffmpegThread->getMediaType(), SaveVideoType_Mp4, videoStreamIn, audioStreamIn);
        this->record();
        timerRecord->start();
    }
}

void NetPushClient::receivePacket(AVPacket *packet)
{
    if (ffmpegSave && ffmpegSave->getIsOk()) {
        ffmpegSave->writePacket2(packet);
    }

    FFmpegHelper::freePacket(packet);
}

void NetPushClient::recorderStateChanged(const RecorderState &state, const QString &file)
{
    int width = 0;
    int height = 0;
    int videoStatus = 0;
    int audioStatus = 0;
    if (ffmpegThread) {
        width = ffmpegThread->getVideoWidth();
        height = ffmpegThread->getVideoHeight();
        FFmpegSave *saveFile = ffmpegThread->getSaveFile();
        if (saveFile->getIsOk()) {
            if (saveFile->getVideoIndexIn() >= 0) {
                if (saveFile->getVideoIndexOut() >= 0) {
                    videoStatus = (saveFile->getVideoEncode() ? 3 : 2);
                } else {
                    videoStatus = 1;
                }
            }
            if (saveFile->getAudioIndexIn() >= 0) {
                if (saveFile->getAudioIndexOut() >= 0) {
                    audioStatus = (saveFile->getAudioEncode() ? 3 : 2);
                } else {
                    audioStatus = 1;
                }
            }
        }
    }

    //只有处于录制中才表示正常推流开始
    bool start = (state == RecorderState_Recording);
    emit pushStart(mediaUrl, width, height, videoStatus, audioStatus, start);
}

void NetPushClient::receiveSaveStart()
{
    emit pushChanged(mediaUrl, 0);
}

void NetPushClient::receiveSaveFinsh()
{
    emit pushChanged(mediaUrl, 1);
}

void NetPushClient::receiveSaveError(int error)
{
    emit pushChanged(mediaUrl, 2);
}

void NetPushClient::setMediaUrl(const QString &mediaUrl)
{
    this->mediaUrl = mediaUrl;
}

void NetPushClient::setPushUrl(const QString &pushUrl)
{
    this->pushUrl = pushUrl;
}

void NetPushClient::start()
{
    if (ffmpegThread || mediaUrl.isEmpty() || pushUrl.isEmpty()) {
        return;
    }

    //实例化视频采集线程
    ffmpegThread = new FFmpegThread;
    //关联播放开始信号用来启动推流
    connect(ffmpegThread, SIGNAL(receivePlayStart(int)), this, SLOT(receivePlayStart(int)));
    //关联录制信号变化用来判断是否推流成功
    connect(ffmpegThread, SIGNAL(recorderStateChanged(RecorderState, QString)), this, SLOT(recorderStateChanged(RecorderState, QString)));
    //设置播放地址
    ffmpegThread->setMediaUrl(mediaUrl);

    //设置视频模式
#ifdef openglx
    ffmpegThread->setVideoMode(VideoMode_Opengl);
#else
    ffmpegThread->setVideoMode(VideoMode_Painter);
#endif

    //设置通信协议(如果是rtsp视频流建议设置tcp)
    //ffmpegThread->setTransport("tcp");
    //设置硬解码(和推流无关/只是为了加速显示/推流只和硬编码有关)
    //ffmpegThread->setHardware("dxva2");
    //设置缓存大小(如果分辨率帧率码流很大需要自行加大缓存)
    ffmpegThread->setCaching(8192000);
    //设置解码策略(推流的地址再拉流建议开启最快速度)
    //ffmpegThread->setDecodeType(DecodeType_Fastest);

    //设置读取超时时间超时后会自动重连
    ffmpegThread->setReadTimeout(10 * 1000);
    //设置连接超时时间(0表示一直连)
    ffmpegThread->setConnectTimeout(0);
    //设置重复播放相当于循环推流
    ffmpegThread->setPlayRepeat(true);
    //设置默认不播放音频(界面上切换到哪一路就开启)
    ffmpegThread->setPlayAudio(false);
    //设置默认不预览视频(界面上切换到哪一路就开启)
    ffmpegThread->setPushPreview(false);

    //设置保存视频类将数据包信号发出来用于保存文件
    FFmpegSave *saveFile = ffmpegThread->getSaveFile();
    saveFile->setProperty("checkB", checkB);
    saveFile->setSendPacket(recordDuration > 0, false);
    connect(saveFile, SIGNAL(receivePacket(AVPacket *)), this, SLOT(receivePacket(AVPacket *)));
    connect(saveFile, SIGNAL(receiveSaveStart()), this, SLOT(receiveSaveStart()));
    connect(saveFile, SIGNAL(receiveSaveFinsh()), this, SLOT(receiveSaveFinsh()));
    connect(saveFile, SIGNAL(receiveSaveError(int)), this, SLOT(receiveSaveError(int)));

    //如果是本地设备或者桌面录屏要取出其他参数
    VideoHelper::initVideoPara(ffmpegThread, mediaUrl, encodeVideoRatio, encodeVideoScale);

    //设置视频编码格式/视频压缩比率/视频缩放比例
    ffmpegThread->setEncodeVideo((EncodeVideo)encodeVideo);
    ffmpegThread->setEncodeVideoRatio(encodeVideoRatio);
    ffmpegThread->setEncodeVideoScale(encodeVideoScale);

    //启动播放
    ffmpegThread->play();
}

void NetPushClient::stop()
{
    //停止推流和采集并彻底释放对象
    if (ffmpegThread) {
        ffmpegThread->recordStop();
        ffmpegThread->stop();
        ffmpegThread->deleteLater();
        ffmpegThread = NULL;
    }

    //停止录制
    if (ffmpegSave) {
        timerRecord->stop();
        ffmpegSave->stop();
        ffmpegSave->deleteLater();
        ffmpegSave = NULL;
    }
}

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

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

相关文章

[第十六届蓝桥杯 JavaB 组] 真题 + 经验分享

A&#xff1a;逃离高塔(AC) 这题就是简单的签到题&#xff0c;按照题意枚举即可。需要注意的是不要忘记用long&#xff0c;用int的话会爆。 &#x1f4d6; 代码示例&#xff1a; import java.io.*; import java.util.*; public class Main {public static PrintWriter pr ne…

深⼊理解 JVM 执⾏引擎

深⼊理解 JVM 执⾏引擎 其中前端编译是在 JVM 虚拟机之外执⾏&#xff0c;所以与 JVM 虚拟机没有太⼤的关系。任何编程语⾔&#xff0c;只要能够编译出 满⾜ JVM 规范的 Class ⽂件&#xff0c;就可以提交到 JVM 虚拟机执⾏。⾄于编译的过程&#xff0c;如果你不是想要专⻔去研…

iwebsec靶场 文件包含关卡通关笔记11-ssh日志文件包含

目录 日志包含 1.构造恶意ssh登录命令 2.配置ssh日志开启 &#xff08;1&#xff09;配置sshd &#xff08;2&#xff09;配置rsyslog &#xff08;3&#xff09;重启服务 3.写入webshell木马 4.获取php信息渗透 5.蚁剑连接 日志包含 1.构造恶意ssh登录命令 ssh服务…

kafka菜鸟教程

一、kafka原理 1、kafka是一个高性能的消息队列系统&#xff0c;能够处理大规模的数据流&#xff0c;并提供低延迟的数据传输&#xff0c;它能够以每秒数十万条消息的速度进行读写操作。 二、kafka优点 1、服务解耦 &#xff08;1&#xff09;提高系统的可维护性‌ 通过服务…

应用镜像是什么?轻量应用服务器的镜像大全

应用镜像是轻量应用服务器专属的&#xff0c;镜像就是轻量应用服务器的装机盘&#xff0c;应用镜像在原有的纯净版操作系统上集成了应用程序&#xff0c;例如WordPress应用镜像、宝塔面板应用镜像、WooCommerce等应用&#xff0c;阿里云服务器网aliyunfuwuqi.com整理什么是轻量…

深入理解分布式缓存 以及Redis 实现缓存更新通知方案

一、分布式缓存简介 1. 什么是分布式缓存 分布式缓存&#xff1a;指将应用系统和缓存组件进行分离的缓存机制&#xff0c;这样多个应用系统就可以共享一套缓存数据了&#xff0c;它的特点是共享缓存服务和可集群部署&#xff0c;为缓存系统提供了高可用的运行环境&#xff0c…

Spring Boot 中的自动配置原理

2025/4/6 向全栈工程师迈进&#xff01; 一、自动配置 所谓的自动配置原理就是遵循约定大约配置的原则&#xff0c;在boot工程程序启动后&#xff0c;起步依赖中的一些bean对象会自动的注入到IOC容器中。 在讲解Spring Boot 中bean对象的管理的时候&#xff0c;我们注入bean对…

剑指Offer(数据结构与算法面试题精讲)C++版——day16

剑指Offer&#xff08;数据结构与算法面试题精讲&#xff09;C版——day16 题目一&#xff1a;序列化和反序列化二叉树题目二&#xff1a;从根节点到叶节点的路径数字之和题目三&#xff1a;向下的路径节点值之和附录&#xff1a;源码gitee仓库 题目一&#xff1a;序列化和反序…

windows server C# IIS部署

1、添加IIS功能 windows server 2012、windows server 2016、windows server 2019 说明&#xff1a;自带的是.net 4.5 不需要安装.net 3.5 尽量使用 windows server 2019、2016高版本&#xff0c;低版本会出现需要打补丁的问题 2、打开IIS 3、打开iis应用池 .net 4.5 4、添…

【教程】PyTorch多机多卡分布式训练的参数说明 | 附通用启动脚本

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 torchrun 一、什么是 torchrun 二、torchrun 的核心参数讲解 三、torchrun 会自动设置的环境变量 四、torchrun 启动过程举例 机器 A&#…

Neo4j初解

Neo4j 是目前应用非常广泛的一款高性能的 NoSQL 图数据库&#xff0c;其设计和实现专门用于存储、查询和遍历由节点&#xff08;实体&#xff09;、关系&#xff08;边&#xff09;以及属性&#xff08;键值对&#xff09;构成的图形数据模型。它的核心优势在于能够以一种自然且…

音视频小白系统入门课-2

本系列笔记为博主学习李超老师课程的课堂笔记&#xff0c;仅供参阅 课程传送门&#xff1a;音视频小白系统入门课 音视频基础ffmpeg原理 往期课程笔记传送门&#xff1a; 音视频小白系统入门笔记-0音视频小白系统入门笔记-1 课程实践代码仓库&#xff1a;传送门 音视频编解…

Linux:安装 CentOS 7(完整教程)

文章目录 一、简介二、安装 CentOS 72.1 虚拟机配置2.2 安装CentOS 7 三、结语 一、简介 CentOS&#xff08;Community ENTerprise Operating System&#xff09;是一个基于 Linux 的发行版之一&#xff0c;旨在提供一个免费的、企业级的计算平台&#xff0c;因其稳定性、安全…

MATLAB 控制系统设计与仿真 - 34

多变量系统知识回顾 - MIMO system 这一章对深入理解多变量系统以及鲁棒分析至关重要 首先&#xff0c;对于如下系统&#xff1a; 当G(s)为单输入&#xff0c;单输出系统时&#xff1a; 如果&#xff1a; 则&#xff1a; 所以 因此&#xff0c;对于SISO&#xff0c;系统的增…

【网络】通过Samba实现Window挂在Linux服务器路径

有时候我们去进行内网部署时&#xff0c;会遇到客户或者甲方爸爸说&#xff0c;需要将Linux中的某个路径共享出去到Window上&#xff0c;挂载出比如Z:\这种盘符。通过打开Z盘&#xff0c;来查看服务器的指定目录下的数据。 步骤1&#xff1a; 在Linux中安装samba yum install…

架构思维:缓存层场景实战_读缓存(下)

文章目录 Pre业务场景缓存存储数据的时机与常见问题解决方案1. 缓存读取与存储逻辑2. 高并发下的缓存问题及解决方案3. 缓存预热&#xff08;减少冷启动问题&#xff09; 缓存更新策略&#xff08;双写问题&#xff09;1. 先更新缓存&#xff0c;再更新数据库&#xff08;不推荐…

uniapp微信小程序实现sse

微信小程序实现sse 注&#xff1a;因为微信小程序不支持sse请求&#xff0c;因为后台给的是分包的流&#xff0c;所以我们就使用接受流的方式&#xff0c;一直接受&#xff0c;然后把接受的数据拿取使用。这里还是使用uniapp的原生请求。 上代码 //注意&#xff1a;一定要下…

新能源汽车能量流测试的传感器融合技术应用指南

第一部分&#xff1a;核心原理模块化拆解 模块1&#xff1a;多源传感器物理层融合 关键技术&#xff1a; 高精度同步采集架构 采用PXIe-8840控制器同步定时模块&#xff08;NI PXIe-6674T&#xff09;&#xff0c;实现CAN/LIN/模拟量信号的μs级同步光纤电压传感器&#xff0…

人工智能与网络安全:AI如何预防、检测和应对网络攻击?

引言&#xff1a;网络安全新战场&#xff0c;AI成关键角色 在数字化浪潮不断推进的今天&#xff0c;网络安全问题已经成为每一家企业、每一个组织无法回避的“隐形战场”。无论是电商平台、金融机构&#xff0c;还是政府机关、制造企业&#xff0c;都可能面临数据泄露、勒索病毒…

链表知识回顾

类型&#xff1a;单链表&#xff0c;双链表、循环链表 存储&#xff1a;在内存中不是连续存储 删除操作&#xff1a;即让c的指针指向e即可&#xff0c;无需释放d&#xff0c;因为java中又内存回收机制 添加节点&#xff1a; 链表的构造函数 public class ListNode {// 结点…