Qt基础 | UDP通信 | UDP单播、广播、组播的介绍与实现

news2024/11/13 7:56:49

文章目录

  • 一、QUdpSocket 实现 UDP 通信
    • 1.UDP 通信概述
    • 2.UDP 单播和广播
      • 2.1 主窗口类定义和构造函数
      • 2.2 UDP通信实现
    • 3.UDP 组播
      • 3.1 主窗口类定义和构造函数
      • 3.2 组播功能的程序实现


Qt 网络模块:

  • Qt基础 | 主机信息查询 | QHostInfo的介绍和使用 | QNetworkInterface的介绍和使用

  • Qt基础 | TCP通信 | TCP服务端与客户端程序设计 | QTcpServer | QTcpSocket

  • Qt基础 | UDP通信 | UDP单播、广播、组播的介绍与实现

  • Qt基础 | 基于HTTP协议的网络文件下载


一、QUdpSocket 实现 UDP 通信

1.UDP 通信概述

  UDP(用户数据报协议)是轻量的、不可靠的、面向数据报、无连接的协议,它可以用于对可靠性要求不高的场合。与 TCP 通信不同,两个程序之间进行 UDP 通信无需预先建立持久的 socket 连接,UDP 每次发送数据报都需要指定目标地址和端口。

image-20240730152941613

  QUdpSocket 类用于实现 UDP 通信,它从 QAbstractSocket 类继承,因而与 QTcpScoket 共享大部分的接口函数。主要区别是 QUdpSocket 以数据报传输数据,而不是以连续的数据流。QUdpSocket 类从 QAbstractSocket 类继承而来,但是又定义了较多新的功能函数用于实现 UDP 特有的一些功能,如数据报读写和多播通信功能。QUdpSocket 没有定义新的信号。

QUdpSocket 的主要功能函数

image-20240730155004096

  发送数据报使用函数 QUdpSocket::writeDatagram(),数据报的长度一般少于 512 个字节,每个数据报包含发送者和接受者的 IP 地址和端口等信息。

  要进行 UDP 数据接收,要用 QUdpSocket::bind() 函数先绑定一个端口,用于接收传入的数据报。当有数据报传入时会发射 readyRead() 信号,在其槽函数中使用 readDatagram() 函数来读取接收到的数据报

  UDP 消息传送有单播、广播、组播三种方式,

image-20240730154514021

  • 单播(unicast) 模式:一个 UDP 客户端发出的数据报只发送到另一个指定地址和端口的 UDP 客户端,是一对一的数据传输。

  • 广播(broadcast)模式:一个 UDP 客户端发出的数据报,在同一网络范围内其他所有的 UDP 客户端都可以收到。QUdpSocket 支待 IPv4 广播。广播经常用于实现网络发现的协议。要获取广播数据只需在数据报中指定接收端地址为 QHostAddress::Brodcast,一般的广播地址是 255.255.255.255。

  • 组播(multicast)模式:也称为多播。UDP 客户端加入到另一个组播 IP 地址指定的多播组,成员向组播地址发送的数据报,组内成员都可以接收到,类似于 QQ 群的功能。QUdpSocket::joinMulticastGroup() 函数实现加入多播组的功能, 加入多播组后,UDP 数据的收发与正常的 UDP 数据收发方法一样。

  使用广播和多播模式,UDP可以实现一些比较灵活的通信功能,而 TCP 通信只有单播模式,没有广播和多播模式。所以,UDP 通信虽然不能保证数据传输的准确性,但是具有灵活性,一般的即时通信软件都是基于 UDP 通信的。

  在单播、广播和多播模式下,UDP 程序都是对等的(即两个实例之间可以进行 UDP 通信,这两个实例可以运行在同一台计算机上,也可以运行在不同的计算机上),不像 TCP 通信那样分为客户端和服务器端。多播和广播的实现方式基本相同,只是数据报的目标 IP 地址设置不同,多播模式需要加入多播组,实现方式有较大差异。

2.UDP 单播和广播

UDP 程序应具有以下功能

  • 可以进行 UDP 数据报的发送和接收
  • 两个实例之间可以进行 UDP 通信,这两个实例可以运行在同一台计算机上,也可以运行在不同的计算机上 。
  • 在同一台计算机上运行时,两个运行实例需要绑定不同的端口,例如实例 A 绑定端口 1200,实例 B 绑定端口 3355 。实例 A 向实例 B 发送数据报时,需要指定实例 B 所在主机的 IP 地址、绑定端口作为目标地址和目标端口,这样实例 B 才能接收到数据报。
  • 在不同计算机上运行时,则可以使用相同的端口,因为 IP 地址不同了,不会导致绑定时发生冲突。一般的 UDP 通信程序都是在不同的计算机上运行的,约定一个固定的端口作为通信端口。

2.1 主窗口类定义和构造函数

  主窗口是一个基于 QMainWindow 的应用程序,界面是由 UI 设计器设计,设计如下:

image-20240730160443519

  MainWindow 类的定义如下:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include    <QUdpSocket>
#include    <QLabel>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    QLabel  *LabSocketState;//socket状态显示标签
    QUdpSocket  *udpSocket;//
    QString getLocalIP();//获取本机IP地址

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
//自定义槽函数
    void    onSocketStateChange(QAbstractSocket::SocketState socketState);
    void    onSocketReadyRead();//读取socket传入的数据
//
    void on_actStart_triggered();
    void on_actStop_triggered();
    void on_actClear_triggered();
    void on_btnSend_clicked();
    void on_actHostInfo_triggered();
    void on_btnBroadcast_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

私有变量:

  • udpSocket:用于 UDP 通信的 socket
  • LabSocketState:用于显示的 socket 状态标签

槽函数:

  • onSocketReadyRead() 函数

    这个槽函数与 udpSocket 的 readyRead() 信号关联,用于读取缓冲区的数据报

  • onSocketStateChange() 函数

    这个槽函数与 udpSocket 的 stateChanged() 信号关联,用于显示 udpSocket 当前的状态

构造函数

  构造函数主要完成了 udpSocket 的创建、信号与槽函数的连接。代码如下:

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

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

    QString localIP=getLocalIP();//本机IP
    this->setWindowTitle(this->windowTitle()+"----本机IP:"+localIP);
    ui->comboTargetIP->addItem(localIP);

    udpSocket=new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocket

    connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    onSocketStateChange(udpSocket->state());

    connect(udpSocket,SIGNAL(readyRead()),
            this,SLOT(onSocketReadyRead()));
}

其对应的槽函数如下:

void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{
    switch(socketState)
    {
    case QAbstractSocket::UnconnectedState:
        LabSocketState->setText("scoket状态:UnconnectedState");
        break;
    case QAbstractSocket::HostLookupState:
        LabSocketState->setText("scoket状态:HostLookupState");
        break;
    case QAbstractSocket::ConnectingState:
        LabSocketState->setText("scoket状态:ConnectingState");
        break;

    case QAbstractSocket::ConnectedState:
        LabSocketState->setText("scoket状态:ConnectedState");
        break;

    case QAbstractSocket::BoundState:
        LabSocketState->setText("scoket状态:BoundState");
        break;

    case QAbstractSocket::ClosingState:
        LabSocketState->setText("scoket状态:ClosingState");
        break;

    case QAbstractSocket::ListeningState:
        LabSocketState->setText("scoket状态:ListeningState");
    }
}

void MainWindow::onSocketReadyRead()
{//读取收到的数据报
    while(udpSocket->hasPendingDatagrams())
    {
        QByteArray   datagram;
        datagram.resize(udpSocket->pendingDatagramSize());

        QHostAddress    peerAddr;
        quint16 peerPort;
        udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
        QString str=datagram.data();

        QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";

        ui->plainTextEdit->appendPlainText(peer+str);
    }
}

2.2 UDP通信实现

  要实现 UDP 数据的接收,必须先用 QUdpSocket::bind() 函数绑定一个端口,用于监听传入的数据报,解除绑定则使用 abort() 函数。

绑定端口

  单击 “绑定端口” 按钮绑定端口后,socket 的状态变为 BoundState。

void MainWindow::on_actStart_triggered()
{//绑定端口
    quint16     port=ui->spinBindPort->value(); //本机UDP端口
    if (udpSocket->bind(port))//绑定端口成功
    {
        ui->plainTextEdit->appendPlainText("**已成功绑定");
        ui->plainTextEdit->appendPlainText("**绑定端口:"
               +QString::number(udpSocket->localPort()));

        ui->actStart->setEnabled(false);
        ui->actStop->setEnabled(true);
    }
    else
        ui->plainTextEdit->appendPlainText("**绑定失败");
}

解除绑定

  单击 “解除绑定”按钮后完成解绑,socket 的状态为 UnconnectedState。

void MainWindow::on_actStop_triggered()
{//解除绑定
    udpSocket->abort(); //解除绑定
    ui->actStart->setEnabled(true);
    ui->actStop->setEnabled(false);
    ui->plainTextEdit->appendPlainText("**已解除绑定");
}

发送消息(数据报)

  发送点对点消息和广播消息都使用 QUdpSocket::writeDatagram() 函数,使用 writeDatagram() 函数向一个目标用户发送消息时,需要指定目标 IP 地址和目标端口。只不过在广播消息时,需要将目标地址更换为一个特殊地址,即广播地址 QHostAddress::Broadcast,一般是 255.255.255.255。QUdpSocket 发送的数据报是 QByteArray 类型的字节数组,数据报的长度一般不超过 512 字节。数据报的内容可以是文本字符串,也可以自定义格式的二进制数据,文本字符串无需以换行符结束。

代码如下:

void MainWindow::on_btnSend_clicked()
{//发送消息 按钮
    QString     targetIP=ui->comboTargetIP->currentText(); //目标IP
    QHostAddress    targetAddr(targetIP);

    quint16     targetPort=ui->spinTargetPort->value();//目标port

    QString  msg=ui->editMsg->text();//发送的消息内容

    QByteArray  str=msg.toUtf8();
    udpSocket->writeDatagram(str,targetAddr,targetPort); //发出数据报

    ui->plainTextEdit->appendPlainText("[out] "+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();
}

void MainWindow::on_btnBroadcast_clicked()
{ //广播消息 按钮
    quint16     targetPort=ui->spinTargetPort->value(); //目标端口
    QString  msg=ui->editMsg->text();
    QByteArray  str=msg.toUtf8();
    udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort);

    ui->plainTextEdit->appendPlainText("[broadcast] "+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();
}

接收消息(数据报)

  QUdpSocket 接收到数据报后发射 readyRead() 信号,在关联的槽函数 onSocketReadyRead() 中调用 readDatagram() 函数来读取缓冲区的数据报。代码如下:

void MainWindow::onSocketReadyRead()
{//读取收到的数据报
    while(udpSocket->hasPendingDatagrams())
    {
        QByteArray   datagram;
        datagram.resize(udpSocket->pendingDatagramSize());

        QHostAddress    peerAddr;
        quint16 peerPort;
        udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
        QString str=datagram.data();

        QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";

        ui->plainTextEdit->appendPlainText(peer+str);
    }
}
  • hasPendingDatagrams() 函数:

    表示是否有待读取的传入数据报

  • pendingDatagramSize() 函数

    返回待读取数据报的字节数

  • readDatagram() 函数

    用于读取数据报的内容,其函数原型为:

    qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)
    
    • 函数说明:

      输入参数 data 和 maxSize 是必须的,表示最多读取 maxSize 字节的数据到变量 data 里。 address 和 port 变量是可选的,用于获取数据报来源的地址和端口。上面的代码中 使用了完整的参数形式,从而可以获得数据报来源的地址 peerAddr 和端口 peerPort。 如果无需获取来源地址和端口,可以采用简略形式,即:

      udpSocket->readDatagram(datagram.data(),datagram.size());
      

由于只是传输字符串,所以简单地将其转换为字符串即可。如果传输的是自定义格式的字符串或二进制数据,需要对接收到的数据进行解析。

运行结果:绑定的端口号是该实例接收数据报的端口

image-20240730165352835

3.UDP 组播

  UDP 组播是主机之间 “一对一组” 的通信模式,当多个客户端加入由一个组播地址定义的多播组之后,客户端向组播地址和端口发送的 UDP 数据报,组内成员都可以接收到,其功能类似于 QQ群组。

  组播报文的目的地址使用 D 类 IP 地址,D 类地址不能出现在 IP 报文的源 IP 地址字段。用同一个 IP 多播地址接收多播数据报的所有主机构成一个组,称为多播组。所有的信息接收者都加入到一个组内,并且一旦加入之后,流向组地址的数据报立即开始向接收者传输,组中的所有成员都能接收到数据报。组中的成员是动态的,主机可以在任何时间加入和离开组。所以,采用 UDP 组播必须使用一个组播地址 。组播地址是 D 类 IP 地址,有特定的地址段。多播组可以是永久的也可以是临时的。多播组地址中,有一部分由官方分配,称为永久多播组。永久多播组保持不变的是它的 IP 地址,组中的成员构成可以发生变化。永久多播组中成员的数量可以是任意的,甚至可以为零。那些没有保留下来的供永久多播组使用的 IP 组播地址, 可以被临时多播组利用。

  关于组播 IP 地址,有如下一些约定:

  • 224.0.0.0~224.0.0.255 为预留的组播地址(永久组地址),地址 224.0.0.0 保留不做分配,其他地址供路由协议使用;

  • 224.0.1.0~224.0.1.255 是公用组播地址,可以用 Internet;

  • 224.0.2.0~238.255.255.255 为用户可用的组播地址(临时组地址),全网范围内有效;

  • 239.0.0.0~239.255.255.255 为本地管理组播地址,仅在特定的本地范围内有效;

所以,若是在家庭或办公室局域网内测试 UDP 组播功能,可以使用的组播地址范围是239.0.0.0~239.255.255.255

  QUdpSocket 支持 UDP 组播,joinMulticastGroup() 函数使主机加入一个多播组,leaveMulticastGroup() 函数使主机离开一个多播组。 UDP 组播的特点是使用组播地址,其他的端口绑定、数据报收发等功能与 UDP 单播完全相同。

UDP 组播程序应具有以下功能

  • 可以发送和接收组播数据报,且在自己主机上发出的数据报,自己也可以接收到

3.1 主窗口类定义和构造函数

  主窗口是一个基于 QMainWindow 的应用程序,界面是由 UI 设计器设计,设计如下:

image-20240730171926626

  主窗口类定义如下:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include    <QMainWindow>

#include    <QUdpSocket>
#include    <QLabel>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

private:
    QLabel  *LabSocketState;
    QUdpSocket  *udpSocket;//用于与连接的客户端通讯的QTcpSocket
    QHostAddress    groupAddress;//组播地址
    QString getLocalIP();//获取本机IP地址
public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
//自定义槽函数
    void    onSocketStateChange(QAbstractSocket::SocketState socketState);
    void    onSocketReadyRead();//读取socket传入的数据
//
    void on_actStart_triggered();
    void on_actStop_triggered();
    void on_actClear_triggered();
    void on_actHostInfo_triggered();
    void on_btnMulticast_clicked();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

私有变量:

  • udpSocket:用于 UDP 通信的 socket
  • LabSocketState:用于显示的 socket 状态标签
  • groupAddress:表示记录组播地址

槽函数:

  • onSocketReadyRead() 函数

    这个槽函数与 udpSocket 的 readyRead() 信号关联,用于读取缓冲区的数据报

  • onSocketStateChange() 函数

    这个槽函数与 udpSocket 的 stateChanged() 信号关联,用于显示 udpSocket 当前的状态

构造函数

  构造函数主要完成了 udpSocket 的创建、信号与槽函数的连接。代码如下:

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

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

    QString localIP=getLocalIP();//本地主机名
    this->setWindowTitle(this->windowTitle()+"----本机IP:"+localIP);

    udpSocket=new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocket
//Multicast路由层次,1表示只在同一局域网内
    //组播TTL: 生存时间,每跨1个路由会减1,多播无法跨过大多数路由所以为1
    //默认值是1,表示数据包只能在本地的子网中传送。
    udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
//    udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,ui->spinTTL->value());

    connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),
            this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
    onSocketStateChange(udpSocket->state());

    connect(udpSocket,SIGNAL(readyRead()),
            this,SLOT(onSocketReadyRead()));
}
  • 使用 QUdpSocket::setSocketOption() 函数,对 socket 进行参数设置

    udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
    

    MulticastTtlOption 是 UDP 组播的数据报的生存期,数据报每跨 1 个路由会减 1。缺省值为 1 ,表示多播数据报只能在同一路由下的局域网内传播。

3.2 组播功能的程序实现

  要进行 UDP 组播通信, UDP 客户端必须先加入 UDP 多播组,也可以随时退出多播组。加入组播之前,必须先绑定端口,绑定端口的语句是:

udpSocket->bind(QHostAddress::AnyIPv4, groupPort, QUdpSocket::ShareAddress)

使用 joinMulticastGroup() 函数加入多播组,使用 leaveMulticastGroup() 函数退出多播组。

  单击 “加入组播” 和 “退出组播” 按钮对应的槽函数:

void MainWindow::on_actStart_triggered()
{//加入组播
    QString     IP=ui->comboIP->currentText();
    groupAddress=QHostAddress(IP);//多播组地址
    quint16     groupPort=ui->spinPort->value();//端口

    if (udpSocket->bind(QHostAddress::AnyIPv4, groupPort, QUdpSocket::ShareAddress))//先绑定端口
    {
        udpSocket->joinMulticastGroup(groupAddress); //加入多播组
        ui->plainTextEdit->appendPlainText("**加入组播成功");
        ui->plainTextEdit->appendPlainText("**组播地址IP:"+IP);
        ui->plainTextEdit->appendPlainText("**绑定端口:"+QString::number(groupPort));
        ui->actStart->setEnabled(false);
        ui->actStop->setEnabled(true);
        ui->comboIP->setEnabled(false);
    }
    else
        ui->plainTextEdit->appendPlainText("**绑定端口失败");
}

void MainWindow::on_actStop_triggered()
{//退出组播
    udpSocket->leaveMulticastGroup(groupAddress);//退出组播
    udpSocket->abort(); //解除绑定
    ui->actStart->setEnabled(true);
    ui->actStop->setEnabled(false);
    ui->comboIP->setEnabled(true);
    ui->plainTextEdit->appendPlainText("**已退出组播,解除端口绑定");
}

发送消息(组播消息)

  加入多播组后,发送组播数据报是使用 writeDatagram() 函数,只是目标地址使用的是组播地址。

void MainWindow::on_btnMulticast_clicked()
{//发送组播消息
    quint16     groupPort=ui->spinPort->value();
    QString  msg=ui->editMsg->text();
    QByteArray  datagram=msg.toUtf8();

    udpSocket->writeDatagram(datagram,groupAddress,groupPort);
//    udpSocket->writeDatagram(datagram.data(),datagram.size(),
//                     groupAddress,groupPort);
    ui->plainTextEdit->appendPlainText("[multicst] "+msg);
    ui->editMsg->clear();
    ui->editMsg->setFocus();
}

接收消息(组播消息)

  在 readyRead() 信号的槽函数里用 readDatagram() 函数来读取数据报。

void MainWindow::onSocketReadyRead()
{//读取数据报
    while(udpSocket->hasPendingDatagrams())
    {
        QByteArray   datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        QHostAddress    peerAddr;
        quint16 peerPort;
        udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);

//        udpSocket->readDatagram(datagram.data(),datagram.size());
        QString str=datagram.data();

        QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";
        ui->plainTextEdit->appendPlainText(peer+str);
    }
}

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

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

相关文章

排序算法:选择排序,golang实现

目录 前言 选择排序 代码示例 1. 算法包 2. 选择排序代码 3. 模拟排序 4. 运行程序 5. 从大到小排序 循环细节 外层循环 内层循环 总结 循环次数测试 假如 10 条数据进行排序 假如 20 条数据进行排序 假如 30 条数据进行排序 选择排序的适用场景 1. 数据规模…

SAP PowerDesigner@官网下载

背景 略 问题 略 解决 用户可以通过访问SAP支持网站的首页&#xff08;‌https://support.sap.com/home.html&#xff09;‌&#xff0c;‌然后导航到“Software Downloads”&#xff08;‌软件下载&#xff09;‌部分来访问SAP软件的下载入口。‌在这里&#xff0c;‌用户可…

HCIP笔记1

hcia复习 osi--开放式系统互联参考模型---7层参考模型 tcp/ip协议栈道---4或5层 osi: 应用层 抽象语言-->编码 表示层 编码-->二进制 会话层 提供应用程序的会话地址 上三层为应用程序对数据流量进行加工及处理的阶段 传输层 分段、端口号 tcp/udp 网…

Apache2 Ubuntu-XXE漏洞渗透

Apache2 Ubuntu-XXE漏洞渗透 Apache2 Ubuntu Default Page 是一个包含xxe漏洞的页面&#xff0c;如何找到和利用xxe漏洞&#xff0c;并找到flag呢&#xff1f; 第一步&#xff1a;先打开其网页 当安装好虚拟机环境后&#xff0c;打开虚拟机我们并不知道它linux的账号密码 因…

通配符https证书的申请途径和配置方法

一、通配符SSL证书的功能 通配符SSL证书&#xff0c;也被称为泛域名证书&#xff0c;是一种特殊类型的SSL证书&#xff0c;它能够保护一个主域名及其所有次级子域名&#xff08;不可跨级保护&#xff09;。例如&#xff0c;如果您的主域名是example.com&#xff0c;那么一个通…

Vue2从基础到实战(v-bind对于样式控制的增强-操作style,v-model在其他表单元素的使用)

v-bind对于样式控制的增强-操作style 语法&#xff1a;style"样式对象" <div class"box" :style"{ CSS属性名1: CSS属性值, CSS属性名2: CSS属性值 }"></div> 代码解析&#xff1a; HTML结构&#xff1a; 包含了一个div元素&…

什么是数据血缘?怎么做好数据血缘分析?

目录 一、什么是数据血缘&#xff1f; 二、数据血缘关系的四大特征 三、数据血缘分析怎么做&#xff1f; 1.定义元数据模型 2.收集元数据 3.建立血缘关系模型 4.追踪数据流动 5.可视化分析 6.集成到数据治理中 7.持续更新和维护 8.应用分析结果 四、数据血缘技术趋势 1.通用的血…

51单片机-第六节-LED点阵屏与_74HC595_

1.LED点阵屏的结构&#xff1a; 与数码管相同&#xff08;数码管只是把LED排成8字结构&#xff09;&#xff0c;8*8的点阵屏有8816个引脚。 双色点阵屏有82*824个引脚&#xff0c;结构如图&#xff1a; 注&#xff1a;点阵屏引脚多为乱序排列&#xff0c; 控制需看单片机说…

基于SpringBoot+Vue的大学生租房系统(带1w+文档)

基于SpringBootVue的大学生租房系统(带1w文档) 基于SpringBootVue的大学生租房系统(带1w文档) 该系统主要实现了用户和房主通过系统注册用户&#xff0c;登录系统后能够编辑自己的个人信息、查看首页&#xff0c;房屋信息&#xff0c;房屋评价&#xff0c;公告资讯&#xff0c;…

Linux第七节课gcc与g++

一、补充权限 普通用户无法执行sudo&#xff1a; 通过sudo执行后显示不在sudoers file中&#xff01;&#xff08;张三不被信任&#xff01;&#xff09; 需要修改配置文件&#xff08;白名单&#xff01;&#xff09; 配置文件位于以下目录&#xff1a; ls /etc/sudoers -…

[Day 40] 區塊鏈與人工智能的聯動應用:理論、技術與實踐

強化學習概述 強化學習&#xff08;Reinforcement Learning, RL&#xff09;是一種機器學習方法&#xff0c;主要用於訓練智能體&#xff08;agent&#xff09;在特定環境&#xff08;environment&#xff09;中進行決策。智能體通過嘗試和錯誤來學習&#xff0c;以最大化其累…

【iOS】—— iOS持久化

iOS持久化 1. 数据持久化的目的2. iOS持久化的方案3. 数据持久化方式的分类内存缓存磁盘缓存 4. 沙盒机制5. 沙盒的目录结构获取应用程序的沙盒路径每次编译代码会生成新的沙盒路径&#xff0c;每次运行获得的沙盒路径都不一样。访问沙盒目录常用C函数介绍沙盒目录介绍 6. 持久…

浅谈线程组插件之bzm - Arrivals Thread Group

浅谈线程组插件之bzm - Arrivals Thread Group bzm - Arrivals Thread Group 是 JMeter 中的一个高级插件&#xff0c;由 BlazeMeter 提供&#xff0c;旨在为性能测试提供更灵活、更贴近实际场景的负载生成方式。与传统的线程组不同&#xff0c;Arrivals Thread Group 通过控制…

网上订餐系统2024((代码+论文+ppt)

网上订餐系统2024((代码论文ppt),编号:sp006 代码经过修正,确保可以运行,下载地址在文末 技术栈: springbottvuemysql 展示: 下载地址: CSDN现在上传有问题,有兴趣的朋友先收藏.正常了贴上下载地址 备注: 专业承接各种程序java,c,c,python,cuda,AI 运行有问题请私信我,…

互联网医院系统源码与医保购药APP开发的完整技术指南

本篇文章&#xff0c;笔者将详细介绍互联网医院系统与医保购药APP的开发全过程&#xff0c;帮助开发者理解其技术要点和实现路径。 一、互联网医院系统开发 1.需求分析与系统设计 需要明确系统的功能需求&#xff0c;如在线问诊、预约挂号、电子病历管理、远程医疗、支付系统…

木材缺陷数据集:从手工模式到智能时代的跨越

亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 引言 …

【运维指南】常见的防火墙端口操作

每当一个应用程序想通过网络访问自己时&#xff0c;它就会申请一个 TCP/IP 端口&#xff0c;这意味着该端口不能被其他任何程序使用。那么&#xff0c;如何检查开放的端口&#xff0c;看看哪个应用程序已经在使用它呢&#xff1f; Windows 查看端口使用情况和进程名称 netst…

详解Qt 之QMdiArea 和 QMdiSubWindow

文章目录 前言QMdiArea概念作用为什么需要 QMdiAreaQMdiArea 的主要函数和成员函数列表 QMdiSubWindow概念作用为什么需要 QMdiSubWindowQMdiSubWindow 的主要函数和成员函数列表 示例代码 更多用法... 总结 前言 在复杂的应用程序中&#xff0c;尤其是那些需要同时管理多个子…

Linux AMBA 驱动:DMA 控制器 PL330 驱动简析

文章目录 1. 前言2. 背景3. PL330 简介4. PL330 驱动加载流程4.1 PL330 设备注册流程4.2 PL330 驱动加载流程 5. 小结6. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. 背景 本文基于…

什么牌子的骨传导耳机好?这五款骨传导耳机用过都是好评!

骨传导耳机&#xff0c;作为听音设备的创新之作&#xff0c;以其独特的传音方式赢得了市场的广泛认可。它不仅让日常听音更加健康舒适&#xff0c;还完美契合了运动场景的需求&#xff0c;让用户在享受音乐的同时&#xff0c;保持对周围环境的敏锐感知。这种设计上的巧思&#…