QT实现在线流媒体播放平台

文章目录

  • QT实现在线流媒体播放平台
    • 简介
    • 开发视频
      • ffmpeg下载
      • SimpleVideoPlayer.h
      • SimpleVideoPlayer.cpp
    • 开发音频
    • 添加功能
      • 打开文件夹
      • 播放暂停
      • 播放上下一首
      • 选择倍速
    • 效果展示
    • 项目下载

QT实现在线流媒体播放平台

简介

Qt是一种流行的C++开发框架,它提供了用于构建图形用户界面(GUI)和跨平台应用程序的工具和库。Qt具有广泛的应用范围,可以用于开发桌面应用程序、移动应用程序和嵌入式系统等。Qt具有丰富的多媒体功能,包括音频和视频处理能力。

在使用Qt进行在线流媒体开发时,我们可以结合FFmpeg和QMediaPlayer来实现强大的音视频处理和播放功能。首先,可以使用FFmpeg解码音视频文件或流,提取音频和视频数据。然后,可以使用QMediaPlayer加载音频和视频数据,并通过其播放控制接口实现音视频的同步播放。此外,可以使用Qt的图形界面开发能力,创建用户友好的界面,显示视频内容,并提供交互控制。

开发视频

FFmpeg是一个开源的多媒体框架,它提供了一套用于处理音视频的工具和库。FFmpeg支持广泛的音视频格式和编解码器,能够对音视频进行解码、编码、转换和处理等操作。在在线流媒体应用中,FFmpeg通常用于处理音视频文件或流,提取其中的音频和视频数据,进行解码和编码,以及应用特效和滤镜等。

ffmpeg下载

如果你的qt版本是32位,下载的FFmpeg是64位,则可能识别不了函数
在这里我提供了64位和32位的FFmpeg下载链接

32位链接: https://pan.baidu.com/s/1wBAv6yYYa_9n64wzmHdO2w
提取码:0703

64位链接: https://pan.baidu.com/s/1aEHWpbTQkhVA30KtfviYjA
提取码:0703

SimpleVideoPlayer.h

#ifndef SIMPLEVIDEOPLAYER_H
#define SIMPLEVIDEOPLAYER_H

#include <QThread>
#include <QMainWindow>
#include <QImage>

extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavformat/avformat.h"
    #include "libavutil/pixfmt.h"
    #include "libswscale/swscale.h"
    #include <libavutil/imgutils.h>
}

class SimpleVideoPlayer : public QThread
{
    Q_OBJECT

public:
    explicit SimpleVideoPlayer();
    ~SimpleVideoPlayer();

    void setVideo(const QString &videoPath)
    {
        this->_videoPath = videoPath;
    }

    inline void play();

    enum class PlayerState {
        Playing,
        Paused,
        Stopped
    };

    void togglePlayPause();
    void changeState();
    void setPlaybackSpeed(QString speed);
signals:
    void sigCreateVideoFrame(QImage image);

protected:
    void run();

private:
    QString _videoPath;
    PlayerState _state = PlayerState::Playing;
    QString speedVideo = "1x";
};

#endif // SIMPLEVIDEOPLAYER_H

SimpleVideoPlayer.cpp

#include "SimpleVideoPlayer.h"
#include <iostream>
#include <QDebug>



SimpleVideoPlayer::SimpleVideoPlayer()
{

}

SimpleVideoPlayer::~SimpleVideoPlayer()
{

}

void SimpleVideoPlayer::play()
{
    start();
}

void SimpleVideoPlayer::togglePlayPause()
{
    if (_state == PlayerState::Playing) {
            _state = PlayerState::Paused;
     } else {
            _state = PlayerState::Playing;
     }
}

void SimpleVideoPlayer::changeState()
{
    if(_state == PlayerState::Playing || _state == PlayerState::Paused)
    {
        _state = PlayerState::Stopped;
    }
}

void SimpleVideoPlayer::setPlaybackSpeed(QString speed)
{
    speedVideo = speed;
}

void SimpleVideoPlayer::run()
{
    _state = PlayerState::Playing;
    if (_videoPath.isNull())
    {
        return;
    }

    char *file1 = _videoPath.toUtf8().data();

    //编解码器的注册,最新版本不需要调用
    //av_register_all();

    //描述多媒体文件的构成及其基本信息
    AVFormatContext *pAVFormatCtx = avformat_alloc_context();

    if (avformat_open_input(&pAVFormatCtx, file1, NULL, NULL) != 0)
    {
        std::cout<<"open file fail"<<std::endl;
        avformat_free_context(pAVFormatCtx);
        return;
    }

    //读取一部分视音频数据并且获得一些相关的信息
    if (avformat_find_stream_info(pAVFormatCtx, NULL) < 0)
    {
        std::cout<<"avformat find stream fail"<<std::endl;
        avformat_close_input(&pAVFormatCtx);
        return;
    }

    int iVideoIndex = -1;

    for (uint32_t i = 0; i < pAVFormatCtx->nb_streams; ++i)
    {
        //视频流
        if (pAVFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            iVideoIndex = i;
            break;
        }
    }

    if (iVideoIndex == -1)
    {
        std::cout<<"video stream not find"<<std::endl;
        avformat_close_input(&pAVFormatCtx);
        return;
    }

    //获取视频流的编解码器上下文的数据结构
    AVCodecContext *pAVCodecCtx = avcodec_alloc_context3(NULL);
    if (!pAVCodecCtx) {
        fprintf(stderr, "Could not allocate codec context\n");
        exit(1);
    }

    if (avcodec_parameters_to_context(pAVCodecCtx, pAVFormatCtx->streams[iVideoIndex]->codecpar) < 0) {
        fprintf(stderr, "Could not copy codec parameters\n");
        exit(1);
    }

    //编解码器信息的结构体
    const AVCodec *pAVCodec = avcodec_find_decoder(pAVCodecCtx->codec_id);

    if (pAVCodec == NULL)
    {
        std::cout<<"not find decoder"<<std::endl;
        return;
    }

    //初始化一个视音频编解码器
    if (avcodec_open2(pAVCodecCtx, pAVCodec, NULL) < 0)
    {
        std::cout<<"avcodec_open2 fail"<<std::endl;
        return;
    }

    //AVFrame 存放从AVPacket中解码出来的原始数据
    AVFrame *pAVFrame = av_frame_alloc();
    AVFrame *pAVFrameRGB = av_frame_alloc();

    //用于视频图像的转换,将源数据转换为RGB32的目标数据
    SwsContext *pSwsCtx = sws_getContext(pAVCodecCtx->width, pAVCodecCtx->height, pAVCodecCtx->pix_fmt,
                                         pAVCodecCtx->width, pAVCodecCtx->height, AV_PIX_FMT_RGB32,
                                         SWS_BICUBIC, NULL, NULL, NULL);
    int iNumBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, pAVCodecCtx->width, pAVCodecCtx->height,1);

    uint8_t *pRgbBuffer = (uint8_t *)(av_malloc(iNumBytes * sizeof(uint8_t)));
    //为已经分配的空间的结构体AVPicture挂上一段用于保存数据的空间
    av_image_fill_arrays(pAVFrameRGB->data, pAVFrameRGB->linesize, pRgbBuffer, AV_PIX_FMT_RGB32, pAVCodecCtx->width, pAVCodecCtx->height,1);
    //AVpacket 用来存放解码之前的数据
    AVPacket packet;
    av_new_packet(&packet, pAVCodecCtx->width * pAVCodecCtx->height);

    //读取码流中视频帧
    while (av_read_frame(pAVFormatCtx, &packet) >= 0)
    {
        if (_state == PlayerState::Stopped) {
            qDebug()<<"当前音乐已终止";
            break;
        }

        if (_state == PlayerState::Paused) {
            QThread::msleep(600); // Sleep for 600ms 线程休息
            continue;
        }

        if (packet.stream_index != iVideoIndex)
        {
            av_packet_unref(&packet);
            continue;
        }

        // 发送数据包到解码器
        if (avcodec_send_packet(pAVCodecCtx, &packet) < 0)
        {
            std::cout << "Error sending packet for decoding." << std::endl;
            av_packet_unref(&packet);
            continue;
        }

        // 从解码器接收解码后的帧
        while (avcodec_receive_frame(pAVCodecCtx, pAVFrame) == 0)
        {
            // 转换像素格式
            sws_scale(pSwsCtx, (uint8_t const * const *)pAVFrame->data, pAVFrame->linesize, 0, pAVCodecCtx->height, pAVFrameRGB->data, pAVFrameRGB->linesize);

            // 构造QImage
            QImage img(pRgbBuffer, pAVCodecCtx->width, pAVCodecCtx->height, QImage::Format_RGB32);

            // 绘制QImage
            emit sigCreateVideoFrame(img);
        }

        av_packet_unref(&packet);
        double fps = av_q2d(pAVFormatCtx->streams[iVideoIndex]->r_frame_rate);
        int delay = 900.0 / fps;
        if(speedVideo == "2x"){
            msleep(delay / 2);
        }
        else if(speedVideo == "0.5x"){
            msleep(delay * 2);
        }
        else{
            msleep(delay);
        }
        //msleep(15);
    }

    //资源回收
    /*av_free(pAVFrame);
    av_free(pAVFrameRGB);
    sws_freeContext(pSwsCtx);
    avcodec_close(pAVCodecCtx);
    avformat_close_input(&pAVFormatCtx);*/
    av_frame_free(&pAVFrame);
    av_frame_free(&pAVFrameRGB);
    sws_freeContext(pSwsCtx);
    avcodec_free_context(&pAVCodecCtx);
    avformat_close_input(&pAVFormatCtx);

}


开发音频

QMediaPlayer是Qt自带的多媒体播放器类,它提供了简单易用的接口,用于播放音频和视频文件。QMediaPlayer支持多种音视频格式,并且可以通过网络流式传输音视频内容。它具有播放、暂停、停止、跳转和调整音量等常见的播放控制功能。此外,QMediaPlayer还提供了信号和槽机制,可用于捕获播放状态的变化和处理用户交互事件。

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("在线播放平台");

    //comboBox 数据居中
    ui->comboBox->setStyleSheet("QComboBox QAbstractItemView { text-align: center; }");
    /*QMediaPlayer * m_player;
    m_player = new QMediaPlayer(this);
    QString strPlayer = "D:/QT.study/onlinePlay/qiFengLe";
    m_player->setMedia(QUrl::fromLocalFile(strPlayer));
    m_player->setVolume(50);
    m_player->play();*/

    //QPushButton *openFileButton = new QPushButton("打开文件", this);
    //setCentralWidget(openFileButton);

    connect(ui->openFileButton, &QPushButton::clicked, this, &MainWindow::openFileDialog);


    _simpleVp = new SimpleVideoPlayer();
    // 创建媒体播放器
    player = new QMediaPlayer;
    //_simpleAp = new audioPlayer();

    connect(_simpleVp,SIGNAL(sigCreateVideoFrame(QImage)),this,SLOT(sigGetVideoFrame(QImage)));
    connect(ui->Slider,&QSlider::valueChanged,this,&MainWindow::changeAudio);
    connect(ui->startBtn,&QPushButton::clicked,this,&MainWindow::startPlay);
    connect(ui->pauseButton , &QPushButton::clicked, this->_simpleVp, &SimpleVideoPlayer::togglePlayPause);

    connect(ui->boxBS, &QComboBox::currentTextChanged, this, &MainWindow::onSpeedChanged);


    //下一首歌曲
    connect(ui->nextButton,&QPushButton::clicked,this->_simpleVp,&SimpleVideoPlayer::changeState);
    connect(ui->nextButton,&QPushButton::clicked,this,&MainWindow::nextVideo);

    //上一首歌曲
    connect(ui->previousBtn,&QPushButton::clicked,this->_simpleVp,&SimpleVideoPlayer::changeState);
    connect(ui->previousBtn,&QPushButton::clicked,this,&MainWindow::previousVideo);

    //暂停播放
    connect(ui->pauseButton,&QPushButton::clicked,this,&MainWindow::pauseAudio);

    //增加MV曲目
    ui->comboBox->addItem("起风了----------买辣椒也用券");
    ui->comboBox->addItem("悬溺------------葛东琪");
    ui->comboBox->addItem("平凡之路--------朴树");
    ui->comboBox->addItem("把回忆拼好给你---------王贰浪");
    ui->comboBox->addItem("带我去找夜生活---------告五人");

    //增加倍速
    ui->boxBS->addItem("1x");
    ui->boxBS->addItem("0.5x");
    ui->boxBS->addItem("2x");

    connect(this, &MainWindow::speedChanged, _simpleVp, &SimpleVideoPlayer::setPlaybackSpeed);
    //视频文件路径
    videoStr["起风了----------买辣椒也用券"] = "D:/QT.music/video/qifengle.mkv";
    videoStr["平凡之路--------朴树"] = "D:/QT.music/video/pingfanzhilu.mkv";
    videoStr["带我去找夜生活---------告五人"] = "D:/QT.music/video/yeshenghuo.mkv";
    videoStr["把回忆拼好给你---------王贰浪"] = "D:/QT.music/video/huiyi.mkv";
    videoStr["悬溺------------葛东琪"] = "D:/QT.music/video/xuanni.mkv";

    //音乐文件路径
    audioStr["起风了----------买辣椒也用券"] = "D:/QT.music/audio/qifengle1.mp3";
    audioStr["平凡之路--------朴树"] = "D:/QT.music/audio/pingfanzhilu.mp3";
    audioStr["带我去找夜生活---------告五人"] = "D:/QT.music/audio/yeshenghuo.mp3";
    audioStr["把回忆拼好给你---------王贰浪"] = "D:/QT.music/audio/huiyi.mp3";
    audioStr["悬溺------------葛东琪"] = "D:/QT.music/audio/xuanni.mp3";

    QIcon icon;
    icon.addFile(tr(":/qrs/4.png"));
    ui->pauseButton->setIconSize(QSize(120,40));
    ui->pauseButton->setIcon(icon);
}

MainWindow::~MainWindow()
{
    delete ui;
    delete _simpleVp;
    delete player;
}

void MainWindow::startPlay()
{
    //获得当前选择歌曲
    QString Str = ui->comboBox->currentText();
    qDebug()<<Str;

    // 加载音乐文件
    QString currentAudio = audioStr[Str];
   // player->setMedia(QUrl::fromLocalFile(currentAudio));

    //0~100音量范围,默认是100
    ui->Slider->setValue(50);
    player->setVolume(50);

    // 加载视频文件
    QString currentVideo = videoStr[Str];
    //_simpleVp->setVideo(currentVideo);

    /*
    _simpleAp->setAudio("D:/QT.study/onlinePlay/qifengle1.mp3");
    开始播放,QThread类的成员函数
    _simpleAp->start();
    */

    //开始播放
    player->play();
    _simpleVp->start();


}

因为需要里面需要打开的文件位置,所以打开时需要更改位置
在这里插入图片描述

添加功能

我们目前只是完成了打开视频和音频的功能,还需要完善一下功能:

  • 打开文件夹
  • 播放暂停
  • 播放下一首歌曲
  • 开启倍速模式
  • 开启调节音量模式

打开文件夹

void MainWindow::openFileDialog()
{
    //QString filePath = QFileDialog::getOpenFileName(this, "Open Song", "", "Audio Files (*.mp3 *.wav);;All Files (*)");
    QString filePath = QFileDialog::getOpenFileName(this, "Open Video", "", "Video Files (*.mp4 *.avi *.mkv *.mov);;All Files (*)");
    ui->openFilePath->setText(filePath);
    if (!filePath.isEmpty())
    {
        // 这里处理用户选择的歌曲,例如将其传递给播放器等。
        qDebug() << "Selected song:" << filePath;
        _simpleVp->setVideo(filePath);
        QString str;
        for(auto it = videoStr.begin();it != videoStr.end();it++)
        {
            if(it->second == filePath)
            {
                str = it->first;
                break;
            }
        }
        player->setMedia(QUrl::fromLocalFile(audioStr[str]));
    }
}

播放暂停

void MainWindow::pauseAudio()
{
    QIcon icon;

    if (player->state() == QMediaPlayer::PlayingState) {
        icon.addFile(tr(":/qrs/3.png"));
        player->pause();
        //ui->pauseButton->setText("Play");
        ui->pauseButton->setIconSize(QSize(120,40));
        ui->pauseButton->setStyleSheet("background-color: transparent;");
        ui->pauseButton->setFlat(true);
        ui->pauseButton->setIcon(icon);
    } else {
        icon.addFile(tr(":/qrs/4.png"));
        player->play();
        //ui->pauseButton->setText("Pause");

        ui->pauseButton->setIconSize(QSize(120,40));
        ui->pauseButton->setStyleSheet("background-color: transparent;");
        ui->pauseButton->setFlat(true);
        ui->pauseButton->setIcon(icon);
    }
}

播放上下一首

void MainWindow::nextVideo() //播放下一首歌曲
{
    int idx = 0;
    QString fileStr = ui->openFilePath->text();
    for(auto it = videoStr.begin();it != videoStr.end();it++)
    {
        if(it->second == fileStr)
        {
            break;
        }
        idx++;
    }
    idx = (idx + 1) % 5;
    int idx1 = idx;
    QString videoFile;                 //= (videoStr.begin() + idx)->second;
    QString audioFile;                 //= (audioStr.begin() + idx)->second;
    for(auto it = videoStr.begin();it != videoStr.end();it++)
    {
        if(idx == 0)
        {
            videoFile = it->second;
            break;
        }
        idx--;
    }
    for(auto it = audioStr.begin();it != audioStr.end();it++)
    {
        if(idx1 == 0)
        {
            audioFile = it->second;
            break;
        }
        idx1--;
    }
    ui->openFilePath->setText(videoFile);
    _simpleVp->setVideo(videoFile);
    player->setMedia(QUrl::fromLocalFile(audioFile));
    _simpleVp->start();
    player->play();
}

//实现上一首歌曲功能
void MainWindow::previousVideo()
{
    int idx = 0;
    QString fileStr = ui->openFilePath->text();
    for(auto it = videoStr.begin();it != videoStr.end();it++)
    {
        if(it->second == fileStr)
        {
            break;
        }
        idx++;
    }
    idx = (idx - 1 + 5) % 5;
    int idx1 = idx;
    QString videoFile;                 //= (videoStr.begin() + idx)->second;
    QString audioFile;                 //= (audioStr.begin() + idx)->second;
    for(auto it = videoStr.begin();it != videoStr.end();it++)
    {
        if(idx == 0)
        {
            videoFile = it->second;
            break;
        }
        idx--;
    }
    for(auto it = audioStr.begin();it != audioStr.end();it++)
    {
        if(idx1 == 0)
        {
            audioFile = it->second;
            break;
        }
        idx1--;
    }
    ui->openFilePath->setText(videoFile);
    _simpleVp->setVideo(videoFile);
    player->setMedia(QUrl::fromLocalFile(audioFile));
    _simpleVp->start();
    player->play();
}

选择倍速

void MainWindow::onSpeedChanged() //选择不同倍速时,会给视频和音频发射信号
{
    QString SpeedStr = ui->boxBS->currentText();
    if(SpeedStr == "2x"){
        player->setPlaybackRate(2.0);
    }
    else if(SpeedStr == "0.5x")
    {
        player->setPlaybackRate(0.5);
    }
    else {
        player->setPlaybackRate(1.0);
    }
    emit speedChanged(SpeedStr);
}

效果展示

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

项目下载

GitHub下载:
https://github.com/Shy2593666979/onlinePlay

百度网盘下载:
链接:https://pan.baidu.com/s/11NaEgQIrj-Z1FLzbyGcL-w
提取码:0703

歌曲文件下载:
链接:https://pan.baidu.com/s/1qs3CmqWdPT6XrFlY039ULQ
提取码:0703


更多资料尽在 GitHub 欢迎各位读者去Star

⭐学术交流群Q 754410389 持续更新中~~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FPGA_Signal TapII 逻辑分析仪 在线信号波形抓取

FPGA_Signal TapII 逻辑分析仪 在线信号波形抓取 由于一些工程的仿真文件不易产生&#xff0c;所以我们可以利用 quartus 软件自带的 SignalTap 工具对波形进行抓取 对各个信号进行分析处理&#xff0c;让电子器件与FPGA进行正常通讯工作&#xff0c;也验证所绘制的波形图是否一…

Scala库用HTTP爬虫IP代码示例

根据提供的引用内容&#xff0c;sttp.client3和sttp.model库是用于HTTP请求和响应处理的Scala库&#xff0c;可以与各种Scala堆栈集成&#xff0c;提供同步和异步&#xff0c;过程和功能接口。这些库可以用于爬虫程序中&#xff0c;用于发送HTTP请求和处理响应。需要注意的是&a…

JavaScript从入门到精通系列第二十七篇:详解JavaScript中的包装类

大神引荐&#xff1a;作者有幸结识技术大神孙哥为好友获益匪浅&#xff0c;现在把孙哥视频分享给大家 孙哥链接&#xff1a;孙哥个人主页 作者简介&#xff1a;一个颜值99分&#xff0c;只比孙哥差一点的程序员 本专栏简介&#xff1a;话不多说&#xff0c;让我们一起干翻JavaS…

vue自定义组件:中线分割拖动盘

在GitHub上可以找到类似的组件&#xff0c;比如4年前发布的vue2版本的 Vue Split Pane&#xff0c; 但是我还是自己写了一个类似的&#xff1a; 组件效果&#xff1a; 特点&#xff1a; 不是照抄别人的。同时支持vue2、vue3&#xff08;组件内部使用选项式API风格&#xff09…

不一样的网络协议-------KCP协议

1、kcp 的协议特点 1.1、RTO 不翻倍 RTO(Retransmission TimeOut)&#xff0c;重传超时时间。tcp x 2&#xff0c;kcp x 1.5&#xff0c;提高传输速度 1.2、选择重传 TCP丢包时会全部重传从该包开始以后的数据&#xff0c;而KCP选择性重传&#xff0c;只重传真正丢失的数据包…

同为科技(TOWE)自动断电倒计时定时桌面PDU插排

在每个家庭中&#xff0c;插排插座都是必不可少的电源设备。随着各种电器的普及应用和生活节奏的加快&#xff0c;人们对插排也有着多样化的需求&#xff0c;比如在插排中加入定时开关、自动断电、断电记忆、倒计时等等功能&#xff0c;让原本不支持智能家居的用电器秒变智能。…

DL4J无法下载MNIST数据集解决 Server returned HTTP response code: 403 for URL解决方法

报错情况 报错如下&#xff1a; 16:45:41.463 [main] INFO org.nd4j.nativeblas.Nd4jBlas - Number of threads used for OpenMP BLAS: 6 16:45:41.497 [main] INFO org.nd4j.linalg.api.ops.executioner.DefaultOpExecutioner - Backend used: [CPU]; OS: [Windows 10] 16:4…

Android 驱动学习调试

1 Android 驱动代码编译 参考https://www.sharetechnote.com/html/Linux_DeviceDriver_Programing.html#Device_Driver_HelloWorld编译ko文件调试驱动代码&#xff0c;将ko文件push到手机上验证 相关C文件testdriver.c #include <linux/init.h> #include <linux/mod…

云安全—kubelet攻击面

0x00 前言 虽然说总结的是kubelet的攻击面&#xff0c;但是在总结攻击面之前还是需要去了解kubelet的基本原理&#xff0c;虽然说我们浅尝即止&#xff0c;但是还是要以能给别人讲出来为基本原则。 其他文章: 云安全—K8s APi Server 6443 攻击面云安全—K8S API Server 未授…

08-Docker-网络管理

Docker 在网络管理这块提供了多种的网络选择方式&#xff0c;他们分别是桥接网络、主机网络、覆盖网络、MACLAN 网络、无桥接网络、自定义网络。 1-无桥接网络&#xff08;None Network&#xff09; 当使用无桥接网络时&#xff0c;容器不会分配 IP 地址&#xff0c;也不会连…

SpringCloud 微服务全栈体系(十)

第十章 RabbitMQ 一、初识 MQ 1. 同步和异步通讯 微服务间通讯有同步和异步两种方式&#xff1a; 同步通讯&#xff1a;就像打电话&#xff0c;需要实时响应。 异步通讯&#xff1a;就像发邮件&#xff0c;不需要马上回复。 两种方式各有优劣&#xff0c;打电话可以立即得…

Sci Immunol丨Tim-3 适配器蛋白 Bat3 是耐受性树突状细胞

今天和大家分享一篇发表于2022年3月的文章&#xff0c;题目为“Tim-3 adapter protein Bat3 acts as an endogenous regulator of tolerogenic dendritic cell function”&#xff0c;发表在《Sci Immunol》杂志上。文章主要研究了Tim-3和其适配蛋白Bat3在调节免疫应答中的作用…

以八数码问题为例实现A*算法的求解(未完结)

八数码&#xff1a; 在一个 33 的网格中&#xff0c;1∼8 这 8 个数字和一个 x 恰好不重不漏地分布在这 33 的网格中。 例如&#xff1a; 1 2 3 x 4 6 7 5 8在游戏过程中&#xff0c;可以把 x 与其上、下、左、右四个方向之一的数字交换&#xff08;如果存在&#xff09;。…

ssh远程登录服务

目录 1.1版本协商阶段 1.2密钥和算法协商阶段 1.3认证阶段(两种认证方法): 2.1.安装ssh 2.2.配置文件分析: 3.1配置ssh监听端口号 3.2拒绝以root身份登录服务器 3.3虚拟机之间实现免密登录 3.4xshell免密登录 SSH (Secure Shell Protocol,安全壳程序协议)由IETF的网络…

厦门万宾科技智能井盖监测仪器的作用如何?

越来越多的人们希望改善生活&#xff0c;走出农村走出大山&#xff0c;前往城市之中居住。由此城市的人口和车辆在不断增加&#xff0c;与之而来的是城市的交通压力越来越大&#xff0c;时常会出现道路安全隐患&#xff0c;这给城市未来发展和智慧城市建设都带来一定的难题&…

电脑开机显示器不亮?正确操作分享(4个方法)!

“我的电脑最近不知道为什么&#xff0c;每次开机后显示器都不亮&#xff0c;重启也没反应&#xff0c;有什么方法可以解决该问题吗&#xff1f;快帮帮我&#xff01;” 随着电脑在我们日常生活和工作中的广泛应用&#xff0c;出现问题时需要及时解决。在众多的电脑问题中&…

家政保洁团队服务预约小程序的效果如何

家政保洁可以是大公司也可以是小团队&#xff0c;但无论什么规格&#xff0c;其市场需求都是稳中有增&#xff0c;随着人们生活品质提升&#xff0c;其对居住环境/办公环境等都有一定要求&#xff0c;这意味着家政团队可以拓展同城乃至外地多个领域的生意。 但线下信息单一&am…

【Linux】第九站:make和makefile

文章目录 一、 Linux项目自动化构建工具make/Makefile1.make/makefile工作现象2.依赖关系与依赖方法3.如何清理4.为什么这里我们需要带上clean5.连续的make6.特殊符号 二、Linux下实现一个简单的进度条1.回车换行2.缓冲区3.倒计时的实现 一、 Linux项目自动化构建工具make/Make…

Mybatis—XML配置文件、动态SQL

学习完Mybatis的基本操作之后&#xff0c;继续学习Mybatis—XML配置文件、动态SQL。 目录 Mybatis的XML配置文件XML配置文件规范XML配置文件实现MybatisX的使用 Mybatis动态SQL动态SQL-if条件查询 \<if\>与\<where\>更新员工 \<set\>小结 动态SQL-\<forea…

RHCSA -- VMware虚拟机配置及破解密码

一、配置虚拟机 1、开启VMware&#xff08;自定义&#xff09; 2、设置虚拟机硬件兼容性&#xff08;默认&#xff09; 3、稍后安装虚拟机操作系统 4、选择为Linux的虚拟机 5、虚拟机机名 6、设置虚拟机处理器 7、设置虚拟机所连接的网络类型 8、选择磁盘类型 9、设置所选磁…
最新文章