QT网络(二):TCP通信

传输层概念

传输控制协议(transmission control protocol,TCP)是一种被大多数 Internet 网络协议用于数据传输的底层网络协议,它是可靠的、面向流和连接的传输协议,特别适合用于连续数据传输。

应用层在网络模型中的层次如下所示:

 传输层管理着端到端的通信,即主机到主机的通信。它的主要职责是为不同主机上的应用程序(占用不同端口)提供数据传输,同时保证这些数据的完整性和可靠性。

网络中还有一个IP协议概念,它与TCP的区分如下图所示:

IP协议属于网络层,是在TCP的报文基础上多了一个头部信息,例如在下列通信过程中,发送和接收的信息在每一层中通过该层的协议进行封包和拆包,最终实现通信。

通常IP层协议已经封装在操作系统中的IP协议栈中用于实现PC---PC的通信(这里的PC是不同的PC),开发QT应用程序时,实际上是PC上的进程,此时使用的是TCP和UDP,实现的是PC(A)----PC(B)的通讯(这里的PC可以是不同的PC,也可以是同一台PC,同一台时就是所谓的IPC主机内通信),A、B为计算机中的进程(可以理解为QT应用程序)。

TCP是面向连接的可靠传输协议,因此TCP 通信必须先建立 TCP 连接,通信端分为客 户端和服务器端Qt 提供 QTcpSocket 类和 QTcpServer 类,用于设计 TCP 通信应用程序。 服务器端程序必须使用 QTcpServer 进行端口监听,建立服务器;使用 QTcpSocket 建立连接,然后使用套接字(socket)进行通信。

相关的类介绍

QTcpServer类

QTcpServer 主要用于在服务器端进行网络监听,创建网络 socket 连接。该类提供的主要函数接口如下表所示,表中列出了函数的返回值,省略了函数的输入参数。

函数 listen()开始服务器端监听,可以设置监听的 IP 地址和端口,一般一个服务器端程序只监听某个端口的网络连接。

当有新的客户端接入时,QTcpServer 内部的函数 incomingConnection()会创建一个与客户端 连接的 QTcpSocket 对象,然后发射 newConnection()信号。在与 newConnection()信号关联的槽函数中,我们可以用 nextPendingConnection()接受客户端的连接,然后使用 QTcpSocket 对象与客户端通信。

所以最后数据通信是通过QTcpSocket类对象完成的,QTcpSocket 类提供了 TCP 的接口,可以用 QTcpSocket 类实现标准的网络通信协议,如 POP3、SMTP 和 NNTP 等(这些协议属于上层协议,也就是应用层),也可以设计自定义协议。

QTcpSocket类

由于 QTcpSocket继承了QAbstractSocket类,而QAbstractSocket又继承了QIODevice类,因此QTcpSocket是一种I/O类设备,具有流数据读写功能,其他大部分的功能函数都继承自QAbstractSocket类。QAbstractSocket类的主要接口函数如下表(省略了函数的输入参数)

TCP 客户端使用 QTcpSocket 对象与 TCP 服务器端建立连接并通信。客户端的 QTcpSocket 对象首先通过 connectToHost()尝试连接到服务器,该函数需要指定服务器的 IP 地址和端口。函数 connectToHost()以异步方式连接到服务器,不会阻塞程序运行,成功连接后 QTcpSocket 对象发 射 connected()信号。

客户端和服务器端建立 socket 连接后,各自的 QTcpSocket 对象就可以向缓冲区写数据或从接收缓冲区读取数据,实现数据通信。当缓冲区有新数据进入时,QTcpSocket 对象会发射readyRead() 信号,我们一般在此信号的槽函数里读取缓冲区数据。接收与发送是异步工作的,有各自的缓冲区。

TCP 服务器端程序设计

主窗口MainWindow头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include    <QTcpServer>
#include    <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    QLabel  *labListen;         //状态栏标签
    QLabel  *labSocketState;    //状态栏标签
    QTcpServer *tcpServer;             //TCP服务器
    QTcpSocket *tcpSocket=nullptr;     //TCP通讯的Socket
    QString getLocalIP();              //获取本机IP地址
//protected:
//    void    closeEvent(QCloseEvent *event);   //close事件处理函数
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();      //析构函数里需要做一些处理,以保证断开与客户端的连接,并停止网络监听

private slots:
    //自定义槽函数
    void    do_newConnection();         //关联QTcpServer的newConnection()信号
    void    do_socketStateChange(QAbstractSocket::SocketState socketState);
    void    do_clientConnected();       //客户端 socket 已连接
    void    do_clientDisconnected();    //客户端 socket 已断开
    void    do_socketReadyRead();       //读取socket传入的数据
    //UI生成的
    void on_actStart_triggered();     //开始监听
    void on_actStop_triggered();      //停止监听
    void on_actClear_triggered();     //清空文本框
    void on_btnSend_clicked();        //发送消息
    void on_actHostInfo_triggered();  //接收消息
private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

变量 tcpServer 用于建立 TCP 服务器,变量tcpSocket 用于与客户端进行 socket 连接和通信。

在主窗口的构造函数中,首先获取本机IP地址并打印,然后创建TCP服务器QTcpServer 对象 tcpServer并将newConnection() 信号与自定义槽函数 do_newConnection()关联。

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    labListen=new QLabel("监听状态:");
    labListen->setMinimumWidth(150);
    ui->statusBar->addWidget(labListen);

    labSocketState=new QLabel("Socket状态:");
    labSocketState->setMinimumWidth(200);
    ui->statusBar->addWidget(labSocketState);

    QString localIP=getLocalIP();   //获取本机IP地址
    this->setWindowTitle(this->windowTitle()+"----本机IP地址:"+localIP);
    ui->comboIP->addItem(localIP);

    tcpServer=new QTcpServer(this); //创建TCP服务器
    connect(tcpServer,SIGNAL(newConnection()),this,SLOT(do_newConnection()));
}

QString MainWindow::getLocalIP()
{//获取本机IPv4地址
    QString hostName=QHostInfo::localHostName();    //本地主机名
    QHostInfo hostInfo=QHostInfo::fromName(hostName);
    QString   localIP="";

    QList<QHostAddress> addList=hostInfo.addresses();  //本机IP地址列表
    if (addList.isEmpty())
        return localIP;

    foreach(QHostAddress aHost, addList)
        if (QAbstractSocket::IPv4Protocol==aHost.protocol())
        {
    //如果本机装有虚拟机软件,设置了VMnetx以太网适配器,可能会返回这些网络的地址
            localIP=aHost.toString();
            // break;
            qDebug()<<localIP<<Qt::endl;
        }
    return localIP;
}

获取本机IP地址使用往期博客讲解的localHostName()和fromName()来从主机名中获取了QHostInfo类型的对象,通过改对象的addresses()韩素华就可以获取本机IP地址列表,

这里需要注意的是,例程中直接返回了找到的第一个IPv4地址。如果有多个IPv4地址还需要根据其他条件判断来取舍。

在析构函数中,应确保窗口关闭时断开与 TCP 客户端的socket 连接,停止 TCP 服务器的网络监听。(先断开与客户端的连接,再停止监听)

MainWindow::~MainWindow()
{//析构函数
    if (tcpSocket != nullptr)
    {
        if (tcpSocket->state()==QAbstractSocket::ConnectedState)
            tcpSocket->disconnectFromHost();    //断开与客户端的连接
    }

    if (tcpServer->isListening())
        tcpServer->close();     //停止网络监听

    delete ui;
}

开始监听

TCP服务器可以使用QTcpServer::listen()函数在本机某个 IP 地址和端口上开始 TCP监听,以等待 TCP 客户端的接入。对应“开始监听”按钮的槽函数如下:

void MainWindow::on_actStart_triggered()
{//"开始监听"按钮
    QString  IP=ui->comboIP->currentText();  //IP地址字符串,如"127.0.0.1"
    quint16  port=ui->spinPort->value();     //端口
    QHostAddress   address(IP);
    tcpServer->listen(address,port);    //开始监听
    //    tcpServer->listen(QHostAddress::LocalHost,port);// Equivalent to QHostAddress("127.0.0.1").
    ui->textEdit->appendPlainText("**开始监听...");
    ui->textEdit->appendPlainText("**服务器地址:"+tcpServer->serverAddress().toString());
    ui->textEdit->appendPlainText("**服务器端口:"+QString::number(tcpServer->serverPort()));

    ui->actStart->setEnabled(false);
    ui->actStop->setEnabled(true);
    labListen->setText("监听状态:正在监听");
}

函 数 listen()的原型定义如下:

bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)

参数 address 是 QHostAddress 类型的主机地址,可以像程序中那样通过 IP 地址字符串创建 QHostAddress 对象。参数 port 是监听的端口号。

TCP监听本机的时候,可以使用本机地址127.0.0.1或常量 QHostAddress::LocalHost,抑或是

本机的实际 IP 地址。

与客户端建立sockect连接

tcpServer 开始监听后,TCP 客户端程序就可以通过 IP 地址和端口连接到此服务器。当有客户端 接入时,tcpServer 会发射 newConnection()信号。其槽函数槽函数 do_newConnection()对应的代码如下所示:

void MainWindow::do_newConnection()
{
    tcpSocket = tcpServer->nextPendingConnection(); //创建socket

    connect(tcpSocket, SIGNAL(connected()),this, SLOT(do_clientConnected()));
    do_clientConnected();   //执行一次槽函数,显示状态

    connect(tcpSocket, SIGNAL(disconnected()),this, SLOT(do_clientDisconnected()));

    connect(tcpSocket,&QTcpSocket::stateChanged,this,&MainWindow::do_socketStateChange);
    do_socketStateChange(tcpSocket->state());   //执行一次槽函数,显示状态

    connect(tcpSocket,SIGNAL(readyRead()),  this,SLOT(do_socketReadyRead()));
}

程序通过QTcpServer::nextPendingConnection()函数获取与连接的客户端进行通信的 QTcpSocket 对象 tcpSocket,并将tcpSocket 的几个信号与相应的槽函数连接起来用于处理与客户端的通信。

服务器与客户端连接后tcpSocket会发射connected()信号,该信号的槽函数如下:

void MainWindow::do_clientConnected()
{//客户端接入时
    ui->textEdit->appendPlainText("**client socket connected");
    ui->textEdit->appendPlainText("**peer address:"+tcpSocket->peerAddress().toString());
    ui->textEdit->appendPlainText("**peer port:"+QString::number(tcpSocket->peerPort()));
}

示例中用于打印相关状态。

服务器与客户端断开时,tcpSocket会发送disconnected()信号,该信号的槽函数如下:

void MainWindow::do_clientDisconnected()
{//客户端断开连接时
    ui->textEdit->appendPlainText("**client socket disconnected");
    tcpSocket->deleteLater();
}

当Sockect状态发生改变时,会发送stateChanged()信号,该信号对应的槽函数如下:

void MainWindow::do_socketStateChange(QAbstractSocket::SocketState socketState)
{//socket状态变化时
    switch(socketState)
    {
    case QAbstractSocket::UnconnectedState:
        labSocketState->setText("socket状态:UnconnectedState");
        break;
    case QAbstractSocket::HostLookupState:
        labSocketState->setText("socket状态:HostLookupState");
        break;
    case QAbstractSocket::ConnectingState:
        labSocketState->setText("socket状态:ConnectingState");
        break;
    case QAbstractSocket::ConnectedState:
        labSocketState->setText("socket状态:ConnectedState");
        break;
    case QAbstractSocket::BoundState:
        labSocketState->setText("socket状态:BoundState");
        break;
    case QAbstractSocket::ClosingState:
        labSocketState->setText("socket状态:ClosingState");
        break;
    case QAbstractSocket::ListeningState:
        labSocketState->setText("socket状态:ListeningState");
    }
}

停止监听

TCP服务器停止监听

停止监听使用QTcpServer::close()函数,在停止监听前断开与客户端的连接,槽函数如下:

void MainWindow::on_actStop_triggered()
{//"停止监听"按钮
    if (tcpServer->isListening())   //tcpServer正在监听
    {
        if (tcpSocket != nullptr)
            if (tcpSocket->state()==QAbstractSocket::ConnectedState)
                tcpSocket->disconnectFromHost();
        tcpServer->close();         //停止监听
        ui->actStart->setEnabled(true);
        ui->actStop->setEnabled(false);
        labListen->setText("监听状态:已停止监听");
    }
}

与客户端数据通信

TCP 服务器端和客户端通过 QTcpSocket 对象通信时,需要规定两者的通信协议,即传输的数据内容如何解析。QTcpSocket 间接继承自 QIODevice,因此支持流数据读写功能。

对于数据的传输有两种协议:基于行、基于块

基于行的数据通信协议一 般用于纯文本数据的通信,每一行数据以一个换行符结束。函数canReadLine()判断是否有新的一 行数据需要读取,如果有就可以用函数 readLine()读取一行数据。基于块的数据通信协议一般用于二进制数据的传输,需要自定义具体的格式。

示例程序中发送的是纯文本数据,使用的是基于行的传输模式,服务器向客户端发送消息如下:

void MainWindow::on_btnSend_clicked()
{//"发送消息"按钮,发送一行字符串,以换行符结束
    QString  msg=ui->editMsg->text();
    ui->textEdit->appendPlainText("[out] "+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();

    QByteArray  str=msg.toUtf8();
    str.append('\n');       //添加一个换行符
    tcpSocket->write(str);
}

程序中将文本输入框中的QString类型字符串转换为QByteArray 类型的字节数组 str,然后在 str 最后面添加 一个换行符,用 QIODevice::write()函数将 str 写入缓冲区,这样就能向客户端发送一行文字。

当客户端发送数据时,服务器端缓冲区有新数据进入,QTcpSocket 对象会发射 readyRead()信号,该信号对应的槽函数是do_socketReadyRead(),一般在这个槽函数中进行接收数据的处理:

void MainWindow::do_socketReadyRead()
{//读取缓冲区的行文本
    while(tcpSocket->canReadLine())
        ui->textEdit->appendPlainText("[in] "+tcpSocket->readLine());
}

每当缓冲区数据攒够一行时(检测到换行符),就通过tcpSocket->readLine()获取一行数据。

TCP Server 通过上述方式与TCP Client 进行双向通信,且这个连接一直存在,直到某一方的 QTcpSocket 对象调用函数 disconnectFromHost()断开 socket 连接。

TCP客户端程序设计

主窗口设计

客户端主窗口与服务器窗口类似,头文件如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include    <QTcpSocket>
#include    <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT
private:
    QTcpSocket  *tcpClient;   //socket
    QLabel  *labSocketState;  //状态栏显示标签
    QString  getLocalIP();    //获取本机IP地址
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
//自定义槽函数
    void    do_connected();
    void    do_disconnected();
    void    do_socketStateChange(QAbstractSocket::SocketState socketState);
    void    do_socketReadyRead();//读取socket传入的数据
    void on_actConnect_triggered();     //连接服务器
    void on_actDisconnect_triggered();  //断开服务器
    void on_actClear_triggered();       //清空文本框
    void on_btnSend_clicked();          //向服务器发送消息
private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

Mainwindow中也定义了一个用于 socket 连接和通信的 QTcpSocket 变量 tcpClient。在主窗口构造函数中,创建 tcpClient,并建立信号与槽函数的关联。相关槽函数与服务端器一致。

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    tcpClient=new QTcpSocket(this);    //创建socket变量
    labSocketState=new QLabel("Socket状态:");  //状态栏标签
    labSocketState->setMinimumWidth(250);
    ui->statusBar->addWidget(labSocketState);
    QString localIP=getLocalIP();      //获取本机IP
    this->setWindowTitle(this->windowTitle()+"----本机IP地址:"+localIP);
    ui->comboServer->addItem(localIP);
    connect(tcpClient,SIGNAL(connected()),   this,SLOT(do_connected()));
    connect(tcpClient,SIGNAL(disconnected()),this,SLOT(do_disconnected()));
    connect(tcpClient,&QTcpSocket::stateChanged,this,&MainWindow::do_socketStateChange);
    connect(tcpClient,SIGNAL(readyRead()),   this,SLOT(do_socketReadyRead()));
}

void MainWindow::do_connected()
{ //connected()信号的槽函数
    ui->textEdit->appendPlainText("**已连接到服务器");
    ui->textEdit->appendPlainText("**peer address:"+
                                   tcpClient->peerAddress().toString());
    ui->textEdit->appendPlainText("**peer port:"+
                                   QString::number(tcpClient->peerPort()));
    ui->actConnect->setEnabled(false);
    ui->actDisconnect->setEnabled(true);
}

void MainWindow::do_disconnected()
{//disConnected()信号的槽函数
    ui->textEdit->appendPlainText("**已断开与服务器的连接");
    ui->actConnect->setEnabled(true);
    ui->actDisconnect->setEnabled(false);
}

void MainWindow::do_socketStateChange(QAbstractSocket::SocketState socketState)
{//stateChange()信号的槽函数
    switch(socketState)
    {
    case QAbstractSocket::UnconnectedState:
        labSocketState->setText("socket状态:UnconnectedState");
        break;
    case QAbstractSocket::HostLookupState:
        labSocketState->setText("socket状态:HostLookupState");
        break;
    case QAbstractSocket::ConnectingState:
        labSocketState->setText("socket状态:ConnectingState");
        break;
    case QAbstractSocket::ConnectedState:
        labSocketState->setText("socket状态:ConnectedState");
        break;
    case QAbstractSocket::BoundState:
        labSocketState->setText("socket状态:BoundState");
        break;
    case QAbstractSocket::ClosingState:
        labSocketState->setText("socket状态:ClosingState");
        break;
    case QAbstractSocket::ListeningState:
        labSocketState->setText("socket状态:ListeningState");
    }
}

void MainWindow::do_socketReadyRead()
{//readyRead()信号的槽函数
    while(tcpClient->canReadLine())
        ui->textEdit->appendPlainText("[in] "+tcpClient->readLine());
}

与服务器建立连接

客户端与服务建立连接,通过QTcpSocket::connectToHost()函数传入服务器地址和端口号,如果服务器此时处于监听状态就会成功建立连接。断开连接使用QTcpSocket::disconnectFromHost()函数即可,相关代码如下:

void MainWindow::on_actConnect_triggered()
{//“连接服务器”按钮
    QString   addr=ui->comboServer->currentText();
    quint16   port=ui->spinPort->value();
    tcpClient->connectToHost(addr,port);
    //    tcpClient->connectToHost(QHostAddress::LocalHost,port);
}

void MainWindow::on_actDisconnect_triggered()
{//“断开连接”按钮
    if (tcpClient->state()==QAbstractSocket::ConnectedState)
        tcpClient->disconnectFromHost();
}

与TCP服务器进行数据通信

和服务器一样,使用QTcpSocket类中提供的函数进行通信,代码部分完全一致:

void MainWindow::on_btnSend_clicked()
{//“发送数据”按钮
    QString  msg=ui->editMsg->text();
    ui->textEdit->appendPlainText("[out] "+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();

    QByteArray  str=msg.toUtf8();
    str.append('\n');
    tcpClient->write(str);
}

void MainWindow::do_socketReadyRead()
{//readyRead()信号的槽函数
    while(tcpClient->canReadLine())
        ui->textEdit->appendPlainText("[in] "+tcpClient->readLine());
}

小结

通过以上TCP服务器和客户端的程序设计可以看出,两者最大的不同部分是建立连接过程,服务器端通过listen()来主动打开监听,而客户端是通过connectToHost()主动连接服务器,其他部分基本都是一样的。

以上示例仅演示了TCP的基本通信,且是一对一的基于连接的通信,而在实际中的 TCP 服务器端程序允许多个客户端接入,为了使每个 socket 连接独立 通信、互不影响,一般采用多线程,即为一个 socket 连接创建一个线程。

参考

QT6 C++开发指南

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

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

相关文章

Cherno C++学习笔记 P43 对象生存周期

这篇文章我们讲一下对象的生存周期。这个也是一个很重要的问题&#xff0c;因为我们总说&#xff0c;编程其实就是在操纵内存&#xff0c;而知道了变量如何在栈上生存&#xff0c;以及我们如何利用这些特性来让我们的编程更加简单&#xff0c;我们才是真的理解了编程。我们都知…

02、10个富士胶片模拟的设置

二色彩 1、色彩的加减控制全局的饱和度增减&#xff1b; 2、色彩效果只提升暖色系饱和度&#xff1b; 3、FX蓝色大幅度提升蓝色系饱和度&#xff1b; 4、三个参数都不改变颜色的色相。 2.1 色彩 色彩调整的是拍摄画面整体的色彩饱和程度 2.2色彩效果 调整的是画面中暖色…

【Unity3D】ILRuntime学习记录一

Unity 2019.4.0f1 导入ILRuntime 2.1.0版本 项目目录/Packages/manifest.json添加如下代码&#xff1a; {"scopedRegistries":[{"name":"ILRuntime","url":"https://registry.npmjs.org","scopes":["com.ou…

ECharts柱状图-柱图38,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个柱状图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供…

2024年12月19日Github流行趋势

项目名称&#xff1a;ByteByteGoHq / system-design-101 项目维护者&#xff1a;slam, LombardiDaniel, Stephanvs, alastairp, lucasberti 等项目介绍&#xff1a;使用视觉和简单术语解释复杂的系统。帮助你准备系统设计面试。项目star数&#xff1a;66,527项目fork数&#xf…

Elasticsearch:什么是信息检索?

信息检索定义 信息检索 (IR) 是一种有助于从大量非结构化或半结构化数据中有效、高效地检索相关信息的过程。信息&#xff08;IR&#xff09;检索系统有助于搜索、定位和呈现与用户的搜索查询或信息需求相匹配的信息。 作为信息访问的主要形式&#xff0c;信息检索是每天使用…

机械鹦鹉与真正的智能:大语言模型推理能力的迷思

编者按&#xff1a; 大语言模型真的具备推理能力吗&#xff1f;我们是否误解了"智能"的本质&#xff0c;将模式匹配误认为是真正的推理&#xff1f; 本文深入探讨了大语言模型&#xff08;LLMs&#xff09;是否真正具备推理能力这一前沿科学问题&#xff0c;作者的核…

day-21 内核链表以及栈

1.昨日作业 1.删除指定节点 找到删除就完事了&#xff0c;双向可以停在删除处。 /***************************** 功能&#xff1a;删除指定结点&#xff08;通过姓名&#xff09;* 参数&#xff1a;phead&#xff1b;oldname; * 返回&#xff1a;成功0&#xff0c;失-1&…

深入剖析MyBatis的架构原理

架构设计 简要画出 MyBatis 的架构图 >> ​​ Mybatis 的功能架构分为哪三层&#xff1f; API 接口层 提供给外部使用的接口 API&#xff0c;开发人员通过这些本地 API 来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。MyBatis 和数据库的…

Figma插件:提高设计工作效率

Figma作为一款当前流行的设计工具&#xff0c;其受欢迎程度的飙升与2020年疫情后的远程工作趋势密切相关。许多设计团队开始转向线上办公模式&#xff0c;这时&#xff0c;Figma的协作功能就显得尤为重要&#xff0c;促使众多设计师从Sketch等传统设计软件转向Figma。 Figma是…

Hive其一,简介、体系结构和内嵌模式、本地模式的安装

目录 一、Hive简介 二、体系结构 三、安装 1、内嵌模式 2、测试内嵌模式 3、本地模式--最常使用的模式 一、Hive简介 Hive 是一个框架&#xff0c;可以通过编写sql的方式&#xff0c;自动的编译为MR任务的一个工具。 在这个世界上&#xff0c;会写SQL的人远远大于会写ja…

Qt之自定义标题栏拓展(十)

Qt开发 系列文章 - user-defined-titlebars&#xff08;十&#xff09; 目录 前言 一、方式一 1.效果演示 2.创建标题栏类 3.可视化UI设计 4.定义相关函数 5.使用标题栏类 二、方式二 1.效果演示 2.创建标题栏类 3.定义相关函数 1.初始化函数 2.功能函数 3.窗口关…

鱼跃医疗获评2024年国家级“绿色工厂”,以绿色制造树立行业标杆

近日&#xff0c;工业和信息化部公布了2024年度绿色制造名单&#xff0c;鱼跃医疗凭借在绿色制造和可持续发展方面的卓越表现&#xff0c;成功入选并获评国家级“绿色工厂”。 “绿色工厂”是工信部为贯彻落实国家《工业绿色发展规划》&#xff0c;加快推动绿色制造体系建设&a…

【数据集】玻璃门窗缺陷检测数据集3085张5类YIOLO+VOC格式

数据集格式&#xff1a;VOC格式YOLO格式 压缩包内含&#xff1a;3个文件夹&#xff0c;分别存储图片、xml、txt文件 JPEGImages文件夹中jpg图片总计&#xff1a;3085 Annotations文件夹中xml文件总计&#xff1a;3085 labels文件夹中txt文件总计&#xff1a;3085 标签种类数&am…

一、LRU缓存

LRU缓存 1.LRU缓存介绍2.LRU缓存实现3.LRU缓存总结3.1 LRU 缓存的应用3.2 LRU 缓存的优缺点 1.LRU缓存介绍 LRU是Least Recently Used 的缩写&#xff0c;意为“最近最少使用”。它是一种常见的缓存淘汰策略&#xff0c;用于在缓存容量有限时&#xff0c;决定哪些数据需要被删…

【视频生成模型】——Hunyuan-video 论文及代码讲解和实操

&#x1f52e;混元文生视频官网 | &#x1f31f;Github代码仓库 | &#x1f3ac; Demo 体验 | &#x1f4dd;技术报告 | &#x1f60d;Hugging Face 文章目录 论文详解基础介绍数据预处理 &#xff08;Data Pre-processing&#xff09;数据过滤 (Data Filtering)数据标注 (Data…

【C++】函数计算题解论

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述&#x1f4af;思路解析3.1 函数的递归定义3.2 边界条件控制3.3 记忆化搜索 &#x1f4af;C实现代码&#x1f4af;添加解释&#x1f4af;小结 &#x1f4af;前言 在…

低温高海拔大载重无人机吊运技术详解

低温高海拔大载重无人机吊运技术是一项复杂而先进的技术&#xff0c;它结合了无人机的飞行控制、吊装系统的操作以及特殊环境下的适应性等多个方面。以下是对该技术的详细解析&#xff1a; 一、无人机基础知识与结构特点 低温高海拔大载重无人机通常采用旋翼设计&#xff0c;…

Java设计模式 —— 【结构型模式】适配器模式(类的适配器、对象适配器、接口适配器)详解

文章目录 基本介绍一、类的适配器二、对象适配器三、接口适配器总结 基本介绍 生活中有很多例子&#xff1a; 不同国家的插座接口不同&#xff0c;需要转换器&#xff1b;家用电源220V&#xff0c;手机只接受5V充电&#xff0c;需要转换器&#xff1b;读卡器&#xff0c;拓展…

系列2:基于Centos-8.6Kubernetes 集成GPU资源信息

每日禅语 自省&#xff0c;就是自我反省、自我检查&#xff0c;自知己短&#xff0c;从而弥补短处、纠正过失。佛陀强调自觉觉他&#xff0c;强调以达到觉行圆满为修行的最高境界。要改正错误&#xff0c;除了虚心接受他人意见之外&#xff0c;还要不忘时时观照己身。自省自悟之…