当前位置: 首页 > news >正文

【QT】UDP通信QUdpSocket(单播、广播、组播)

目录

  • 1. UDP通信概述
  • 2. UDP消息传送的三种模式
  • 3. QUdpSocket类的接口函数
  • 4. UDP单播和广播代码示例
    • 4.1 测试说明
    • 4.2 MainWindow.h
    • 4.3 MainWindow.cpp
    • 4.4 界面展示
  • 5. UDP组播代码示例
    • 5.1 组播的特性
    • 5.2 MainWindow.h
    • 5.3 MainWindow.cpp
    • 5.4 界面展示

1. UDP通信概述

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

QUdpSocket以数据报传输数据,而不是以连续的数据流。发送数据报使用函数QUdpSocket::writeDatagram(),数据报的长度一般少于512字节,每个数据报包含发送者和接收者的IP地址和端口等信息。
UDP数据接收,首先要使用QUdpSocket::bind()绑定一个端口,绑定端口后,socket的状态会变为已绑定状态“BoundState”。当有数据报传入时,QudpSocket会自动发射readyRead()信号,在其槽函数中使用QUdpSocket::readDatagram()进行数据读取。abort()为解除绑定,解除后socket状态变为未连接状态“UnconnectedState”。

2. UDP消息传送的三种模式

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

广播模式(broadcast):一个UDP客户端发出的数据报,在同一网络范围内其他所有的UDP客户端都可以收到。QUdpSocket支持IPv4广播。需要在数据报中指定接收端地址为QHostAddress::Broadcast,一般的广播地址是255.255.255.255

组播模式(multicast):UDP客户端加入到另一个组播IP地址的多播组,成员向组播地址发送的数据报,其加入组播的所有成员都可以接收到,类似于QQ群功能。QUdpSocket::joinMulticastGroup()函数实现加入多播组的功能。

在单播、广播和多播模式下,UDP程序都是对等的,不像TCP通信分为客户端和服务端。
TCP通信只有单播模式。UDP通信虽然不能保证数据传输的准确性,但是具有灵活性,一般的即时通信软件都是基于UDP通信的。

3. QUdpSocket类的接口函数

bool bind(quint16 port = 0) 为UDP通信绑定一个端口

qint64 writeDatagram(QByteArray& datagram, QHostAddress& host, quint16 port) 向目标地址和端口的UDP客户端发送数据报,返回成功发送的字节数,数据报的长度一般不超过512字节。

bool hasPendingDatagrams() 当至少有一个数据报需要读取时,返回true

qint64 pendingDatagramSize() 返回第一个待读取的数据报的大小

qint64 readDatagram(char* data, qint64 maxSize) 读取一个数据报,返回成功读取的字节数

qint64 readDatagram(char* data, qint64 maxSize, QHostAddress* address, quint16* port) 读取一个数据报,返回成功读取的字节数。发送方的主机地址和端口存储在*address和*port中(除非指针为0)

bool joinMulticastGroup(QHostAddress& groupAddress) 加入一个多播组

bool leaveMulticastGroup(QHostAddress& groupAddress) 离开一个多播组

void abort() 终止当前连接并重置套接字。通常在析构函数中写入。与disconnectFromHost()不同,该函数立即关闭套接字,丢弃写入缓冲区中的任何挂起数据。

4. UDP单播和广播代码示例

4.1 测试说明

本实例实现UDP通信的单播和广播。两个实例可以运行在同一台计算机上,也可以运行在不同的计算机上。
这里的两个实例是运行在同一台计算机上,需要注意,在同一台计算机上运行时,两个实例需要绑定不同的端口。例如实例A绑定端口1600,实例B绑定端口3200,实例A向实例B发送数据报时,需要指定实例B的端口,这样实例B才能收到数据。
如果两个实例在不同的计算机上运行,则端口可以一样,因为IP地址不同了,不会导致绑定时发生冲突。一般的UDP通信程序都是在不同的计算机上运行的,约定一个固定的端口作为通信端口。

4.2 MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QAction>
#include <QComboBox>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QHostInfo>
#include <QLabel>
#include <QLineEdit>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QSpinBox>
#include <QUdpSocket>
#include <QVBoxLayout>namespace Ui {class MainWindow;
}class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget* parent = 0);~MainWindow();private slots:void slotActBindPort();void slotActUnbindPort();void slotActClearText();void slotActQuit();void slotSocketStateChanged(QAbstractSocket::SocketState socketState);void slotBtnSend();void slotBtnBroad();void slotSocketReadyRead();  //读取socket传入的数据private:Ui::MainWindow* ui;QAction* m_pActBindPort;QAction* m_pActUnbindPort;QAction* m_pActClearText;QAction* m_pActQuit;QWidget* m_pCentralWidget;QLabel* m_pLabBindPort;QLabel* m_PLabTargetAddr;QLabel* m_pLabTargetPort;QSpinBox* m_pSpinBindPort;QComboBox* m_pComboTargetAddr;QSpinBox* m_pSpinTargetPort;QLineEdit* m_pLineEdit;QPushButton* m_pBtnSend;QPushButton* m_pBtnBroad;QPlainTextEdit* m_pPlainText;QLabel* m_pLabState;QUdpSocket* m_pUdpSocket;QString getLocalIP();
};#endif  // MAINWINDOW_H

4.3 MainWindow.cpp

#include "mainwindow.h"#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);this->setWindowIcon(QIcon(":/new/prefix1/res/TitleIcon.png"));this->setWindowTitle(QStringLiteral("UDP Send/Receiver"));//工具栏ui->mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);m_pActBindPort = new QAction(QIcon(":/new/prefix1/res/绑定端口.png"), QStringLiteral("绑定端口"), this);m_pActUnbindPort = new QAction(QIcon(":/new/prefix1/res/解除绑定.png"), QStringLiteral("结束绑定"), this);m_pActClearText = new QAction(QIcon(":/new/prefix1/res/清空文本.png"), QStringLiteral("清空文本"), this);m_pActQuit = new QAction(QIcon(":/new/prefix1/res/退出.png"), QStringLiteral("退出"), this);ui->mainToolBar->addAction(m_pActBindPort);ui->mainToolBar->addAction(m_pActUnbindPort);ui->mainToolBar->addSeparator();ui->mainToolBar->addAction(m_pActClearText);ui->mainToolBar->addSeparator();ui->mainToolBar->addAction(m_pActQuit);//界面布局m_pCentralWidget = new QWidget(this);QHBoxLayout* HLay1 = new QHBoxLayout;m_pLabBindPort = new QLabel(QStringLiteral("绑定端口"), m_pCentralWidget);m_pSpinBindPort = new QSpinBox(m_pCentralWidget);m_pSpinBindPort->setMinimum(1);m_pSpinBindPort->setMaximum(65535);m_pSpinBindPort->setValue(1600);m_PLabTargetAddr = new QLabel(QStringLiteral("目标地址"), m_pCentralWidget);m_pComboTargetAddr = new QComboBox(m_pCentralWidget);m_pLabTargetPort = new QLabel(QStringLiteral("目标端口"), m_pCentralWidget);m_pSpinTargetPort = new QSpinBox(m_pCentralWidget);m_pSpinTargetPort->setMinimum(1);m_pSpinTargetPort->setMaximum(65535);m_pSpinTargetPort->setValue(3200);HLay1->addWidget(m_pLabBindPort, 1, Qt::AlignRight);HLay1->addWidget(m_pSpinBindPort, 2);HLay1->addWidget(m_PLabTargetAddr, 1, Qt::AlignRight);HLay1->addWidget(m_pComboTargetAddr, 4);HLay1->addWidget(m_pLabTargetPort, 1, Qt::AlignRight);HLay1->addWidget(m_pSpinTargetPort, 2);QHBoxLayout* HLay2 = new QHBoxLayout;m_pLineEdit = new QLineEdit(m_pCentralWidget);m_pBtnSend = new QPushButton(QStringLiteral("发送消息"), m_pCentralWidget);m_pBtnBroad = new QPushButton(QStringLiteral("广播消息"), m_pCentralWidget);HLay2->addWidget(m_pLineEdit);HLay2->addWidget(m_pBtnSend);HLay2->addWidget(m_pBtnBroad);QVBoxLayout* VLay = new QVBoxLayout(m_pCentralWidget);  //主布局必须设置parent,否则不会显示布局// QVBoxLayout* VLay = new QVBoxLayout();VLay->addLayout(HLay1);VLay->addLayout(HLay2);m_pPlainText = new QPlainTextEdit(m_pCentralWidget);VLay->addWidget(m_pPlainText);this->setCentralWidget(m_pCentralWidget);this->setLayout(VLay);  //设置为窗体的主布。在指定了主布局的parent之后,这句话可有可无QString localIP = getLocalIP();this->setWindowTitle(this->windowTitle() + "---IP:" + localIP);m_pComboTargetAddr->addItem(localIP);m_pUdpSocket = new QUdpSocket(this);//状态栏m_pLabState = new QLabel(QStringLiteral("socket状态:"), this);m_pLabState->setMinimumWidth(150);ui->statusBar->addWidget(m_pLabState);// connectconnect(m_pActBindPort, &QAction::triggered, this, &MainWindow::slotActBindPort);connect(m_pActUnbindPort, &QAction::triggered, this, &MainWindow::slotActUnbindPort);connect(m_pActClearText, &QAction::triggered, this, &MainWindow::slotActClearText);connect(m_pActQuit, &QAction::triggered, this, &MainWindow::slotActQuit);connect(m_pBtnSend, &QPushButton::clicked, this, &MainWindow::slotBtnSend);connect(m_pBtnBroad, &QPushButton::clicked, this, &MainWindow::slotBtnBroad);connect(m_pUdpSocket, &QUdpSocket::stateChanged, this, &MainWindow::slotSocketStateChanged);connect(m_pUdpSocket, &QUdpSocket::readyRead, this, &MainWindow::slotSocketReadyRead);
}MainWindow::~MainWindow() {m_pUdpSocket->abort();delete m_pUdpSocket;m_pUdpSocket = nullptr;delete ui;
}void MainWindow::slotActBindPort() {quint16 port = m_pSpinBindPort->value();  //本机UDP端口if (m_pUdpSocket->bind(port)) {m_pPlainText->appendPlainText("**已成功绑定");m_pPlainText->appendPlainText("绑定端口:" + QString::number(m_pUdpSocket->localPort()));//使能m_pActBindPort->setEnabled(false);m_pActUnbindPort->setEnabled(true);} else {m_pPlainText->appendPlainText("绑定失败");}
}void MainWindow::slotActUnbindPort() {m_pUdpSocket->abort();  //解除绑定m_pPlainText->appendPlainText("**已解除绑定");m_pActBindPort->setEnabled(true);m_pActUnbindPort->setEnabled(false);
}void MainWindow::slotActClearText() { m_pPlainText->clear(); }void MainWindow::slotActQuit() {QMessageBox::StandardButton button = QMessageBox::question(this, "", "是否要退出?");if (button == QMessageBox::StandardButton::Yes)this->close();
}void MainWindow::slotSocketStateChanged(QAbstractSocket::SocketState socketState) {switch (socketState) {case QAbstractSocket::UnconnectedState: m_pLabState->setText("socket状态:UnconnectedState"); break;case QAbstractSocket::HostLookupState: m_pLabState->setText("socket状态:HostLookupState"); break;case QAbstractSocket::ConnectingState: m_pLabState->setText("socket状态:ConnectingState"); break;case QAbstractSocket::ConnectedState: m_pLabState->setText("socket状态:ConnectedState"); break;case QAbstractSocket::BoundState: m_pLabState->setText("socket状态:BoundState"); break;case QAbstractSocket::ClosingState: m_pLabState->setText("socket状态:ClosingState"); break;default: break;}
}void MainWindow::slotBtnSend() {QString msg = m_pLineEdit->text();QByteArray str = msg.toUtf8();QString targetIp = m_pComboTargetAddr->currentText();  //目标IPQHostAddress targetAddr(targetIp);quint16 targetPort = m_pSpinTargetPort->value();  //目标端口m_pUdpSocket->writeDatagram(str, targetAddr, targetPort);m_pPlainText->appendPlainText("[out] " + msg);m_pLineEdit->clear();m_pLineEdit->setFocus();
}void MainWindow::slotBtnBroad() {QString msg = m_pLineEdit->text();QByteArray str = msg.toUtf8();quint16 targetPort = m_pSpinTargetPort->value();m_pUdpSocket->writeDatagram(str, QHostAddress::Broadcast, targetPort);m_pPlainText->appendPlainText("[out] " + msg);m_pLineEdit->clear();m_pLineEdit->setFocus();
}void MainWindow::slotSocketReadyRead() {while (m_pUdpSocket->hasPendingDatagrams()) {QByteArray dataGram;dataGram.resize(m_pUdpSocket->pendingDatagramSize());QHostAddress peerAddress;quint16 peerPort;m_pUdpSocket->readDatagram(dataGram.data(), dataGram.size(), &peerAddress, &peerPort);QString str = dataGram.data();QString peer = "[From " + peerAddress.toString() + ":" + QString::number(peerPort) + "]";m_pPlainText->appendPlainText(peer + str);}
}QString MainWindow::getLocalIP() {QString hostName = QHostInfo::localHostName();QHostInfo hostInfo = QHostInfo::fromName(hostName);QString localIP = "";QList<QHostAddress> addrList = hostInfo.addresses();if (!addrList.isEmpty()) {for (int i = 0; i < addrList.size(); i++) {QHostAddress addr = addrList.at(i);if (QAbstractSocket::IPv4Protocol == addr.protocol()) {localIP = addr.toString();break;}}}return localIP;
}

4.4 界面展示

在这里插入图片描述

5. UDP组播代码示例

5.1 组播的特性

组播报文的目的地址使用D类IP地址,关于组播IP地址,有以下约定:

  1. 224.0.0.0 ~ 224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其他地址供路由协议使用。
  2. 224.0.1.0 ~ 224.0.1.255是公用组播地址,可以用于Internet。
  3. 224.0.2.0 ~ 238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效。
  4. 239.0.0.0 ~ 239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。所以,若是在家庭或办公室局域网内测试UDP组播功能,可以使用这些IP。

5.2 MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QAction>
#include <QComboBox>
#include <QHBoxLayout>
#include <QHostInfo>
#include <QLabel>
#include <QLineEdit>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QRegExp>
#include <QSpinBox>
#include <QUdpSocket>
#include <QVBoxLayout>namespace Ui {class MainWindow;
}class MainWindow : public QMainWindow {Q_OBJECTpublic:explicit MainWindow(QWidget* parent = 0);~MainWindow();private slots:void slotActJoinMulti();void slotActLeaveMulti();void slotActClearText();void slotActQuit();void slotSocketStateChanged(QAbstractSocket::SocketState socketState);void slotBtnMultiMsg();void slotReadyRead();private:Ui::MainWindow* ui;QAction* m_pActJoinMulti;QAction* m_pActLeaveMulti;QAction* m_pActClearText;QAction* m_pActQuit;QWidget* m_pCentralWidget;QLabel* m_pLabPort;QLabel* m_pLabAddr;QSpinBox* m_pSpinPort;QComboBox* m_pComboAddr;QLineEdit* m_pLineEdit;QPushButton* m_pBtnSendMulti;QPlainTextEdit* m_pPlainText;QLabel* m_pLabState;QUdpSocket* m_pUdpSocket;QHostAddress m_multicastAddr;QString getLocalIP();
};#endif  // MAINWINDOW_H

5.3 MainWindow.cpp

#include "mainwindow.h"#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);this->setWindowTitle(QStringLiteral("UDP Multicast"));//工具栏ui->mainToolBar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);m_pActJoinMulti = new QAction(QIcon(":/new/prefix1/res/添加群组.png"), QStringLiteral("加入组播"), this);m_pActLeaveMulti = new QAction(QIcon(":/new/prefix1/res/退出群组.png"), QStringLiteral("退出组播"), this);m_pActClearText = new QAction(QIcon(":/new/prefix1/res/清空.png"), QStringLiteral("清空文本"), this);m_pActQuit = new QAction(QIcon(":/new/prefix1/res/退出.png"), QStringLiteral("退出"), this);ui->mainToolBar->addAction(m_pActJoinMulti);ui->mainToolBar->addAction(m_pActLeaveMulti);ui->mainToolBar->addSeparator();ui->mainToolBar->addAction(m_pActClearText);ui->mainToolBar->addSeparator();ui->mainToolBar->addAction(m_pActQuit);//界面布局m_pCentralWidget = new QWidget(this);m_pLabPort = new QLabel(QStringLiteral("组播端口"), m_pCentralWidget);m_pSpinPort = new QSpinBox(m_pCentralWidget);m_pSpinPort->setMinimum(1);m_pSpinPort->setMaximum(65535);m_pSpinPort->setValue(3200);m_pLabAddr = new QLabel(QStringLiteral("组播地址"), m_pCentralWidget);m_pComboAddr = new QComboBox(m_pCentralWidget);m_pComboAddr->setEditable(true);  //下拉框可编辑输入m_pComboAddr->addItem("239.0.0.1");// 正则匹配 D类IP:224.0.0.0~239.255.255.255// .必须使用转义字符\,否则.会匹配任意字符// C++中"\"在字符串中表示要用"\\"// 是 - 不是 ~ ; 是[0-9]不是[0~9]QRegExp regExp("^(22[4-9]|23[0-9])(\\.((\\d)|([1-9]\\d)|(1\\d{2})|(2[0-4]\\d)|(25[0-5]))){3}$");QValidator* pValidator = new QRegExpValidator(regExp, this);m_pComboAddr->setValidator(pValidator);QHBoxLayout* HLay1 = new QHBoxLayout();HLay1->addWidget(m_pLabPort, 1, Qt::AlignRight);HLay1->addWidget(m_pSpinPort, 1);HLay1->addWidget(m_pLabAddr, 1, Qt::AlignRight);HLay1->addWidget(m_pComboAddr, 2);m_pLineEdit = new QLineEdit(m_pCentralWidget);m_pBtnSendMulti = new QPushButton(QStringLiteral("组播消息"), m_pCentralWidget);QHBoxLayout* HLay2 = new QHBoxLayout();HLay2->addWidget(m_pLineEdit, 4);HLay2->addWidget(m_pBtnSendMulti, 1);m_pPlainText = new QPlainTextEdit(m_pCentralWidget);QVBoxLayout* VLay = new QVBoxLayout(m_pCentralWidget);VLay->addLayout(HLay1);VLay->addLayout(HLay2);VLay->addWidget(m_pPlainText);this->setCentralWidget(m_pCentralWidget);this->setLayout(VLay);//状态栏m_pLabState = new QLabel(QStringLiteral("socket状态:"), this);m_pLabState->setMinimumWidth(150);ui->statusBar->addWidget(m_pLabState);QString str = getLocalIP();this->setWindowTitle(this->windowTitle() + "---IP:" + str);m_pUdpSocket = new QUdpSocket(this);m_pUdpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);// connectconnect(m_pActJoinMulti, &QAction::triggered, this, &MainWindow::slotActJoinMulti);connect(m_pActLeaveMulti, &QAction::triggered, this, &MainWindow::slotActLeaveMulti);connect(m_pActClearText, &QAction::triggered, this, &MainWindow::slotActClearText);connect(m_pActQuit, &QAction::triggered, this, &MainWindow::slotActQuit);connect(m_pUdpSocket, &QUdpSocket::stateChanged, this, &MainWindow::slotSocketStateChanged);connect(m_pBtnSendMulti, &QPushButton::clicked, this, &MainWindow::slotBtnMultiMsg);connect(m_pUdpSocket, &QUdpSocket::readyRead, this, &MainWindow::slotReadyRead);
}MainWindow::~MainWindow() { delete ui; }void MainWindow::slotActJoinMulti() {QString ip = m_pComboAddr->currentText();m_multicastAddr = QHostAddress(ip);quint16 multicastPort = m_pSpinPort->value();if (m_pUdpSocket->bind(QHostAddress::AnyIPv4, multicastPort, QUdpSocket::ShareAddress)) {m_pUdpSocket->joinMulticastGroup(m_multicastAddr);  //加入多播组m_pPlainText->appendPlainText("**加入组播成功");m_pPlainText->appendPlainText("**组播地址IP:" + ip);m_pPlainText->appendPlainText("**绑定端口:" + QString::number(multicastPort));m_pActJoinMulti->setEnabled(false);m_pActLeaveMulti->setEnabled(true);m_pComboAddr->setEditable(false);} else {m_pPlainText->appendPlainText("**绑定端口失败");}
}void MainWindow::slotActLeaveMulti() {m_pUdpSocket->leaveMulticastGroup(m_multicastAddr);  //退出组播m_pUdpSocket->abort();                               //解除绑定m_pActJoinMulti->setEnabled(true);m_pActLeaveMulti->setEnabled(false);m_pComboAddr->setEnabled(true);m_pComboAddr->setEditable(true);m_pPlainText->appendPlainText("**已退出组播,解除端口绑定");
}void MainWindow::slotActClearText() { m_pPlainText->clear(); }void MainWindow::slotActQuit() {QMessageBox::StandardButton button = QMessageBox::question(this, "", "是否退出?");if (QMessageBox::StandardButton::Yes == button) {this->close();}
}void MainWindow::slotSocketStateChanged(QAbstractSocket::SocketState socketState) {// case并不包含所有的情况,因为没有写listening的情况,所以就需要写defaultswitch (socketState) {case QAbstractSocket::UnconnectedState: m_pLabState->setText("socket状态:UnconnectedState"); break;case QAbstractSocket::HostLookupState: m_pLabState->setText("socket状态:HostLookupState"); break;case QAbstractSocket::ConnectingState: m_pLabState->setText("socket状态:ConnectingState"); break;case QAbstractSocket::ConnectedState: m_pLabState->setText("socket状态:ConnectedState"); break;case QAbstractSocket::BoundState: m_pLabState->setText("socket状态:BoundState"); break;case QAbstractSocket::ClosingState: m_pLabState->setText("socket状态:ClosingState"); break;default: break;}
}void MainWindow::slotBtnMultiMsg() {QString msg = m_pLineEdit->text();QByteArray str = msg.toUtf8();quint16 multiPort = m_pSpinPort->value();m_pUdpSocket->writeDatagram(str, m_multicastAddr, multiPort);m_pPlainText->appendPlainText("[multicast] " + msg);m_pLineEdit->clear();m_pLineEdit->setFocus();
}void MainWindow::slotReadyRead() {while (m_pUdpSocket->hasPendingDatagrams()) {QByteArray dataGram;dataGram.resize(m_pUdpSocket->pendingDatagramSize());QHostAddress peerAddr;quint16 peerPort;m_pUdpSocket->readDatagram(dataGram.data(), dataGram.size(), &peerAddr, &peerPort);QString str = dataGram.data();QString peer = "[From " + peerAddr.toString() + ":" + QString::number(peerPort) + "] ";m_pPlainText->appendPlainText(peer + str);qDebug() << m_pUdpSocket->peerAddress();qDebug() << m_pUdpSocket->localAddress().toString();qDebug() << m_pUdpSocket->localPort();}
}QString MainWindow::getLocalIP() {QString localName = QHostInfo::localHostName();QHostInfo hostInfo = QHostInfo::fromName(localName);QList<QHostAddress> addrList = hostInfo.addresses();QString localIP = "";if (!addrList.isEmpty()) {for (int i = 0; i < addrList.size(); i++) {QHostAddress addr = addrList.at(i);if (QAbstractSocket::IPv4Protocol == addr.protocol()) {localIP = addr.toString();break;}}}return localIP;
}

5.4 界面展示

在这里插入图片描述

相关文章:

【QT】UDP通信QUdpSocket(单播、广播、组播)

目录1. UDP通信概述2. UDP消息传送的三种模式3. QUdpSocket类的接口函数4. UDP单播和广播代码示例4.1 测试说明4.2 MainWindow.h4.3 MainWindow.cpp4.4 界面展示5. UDP组播代码示例5.1 组播的特性5.2 MainWindow.h5.3 MainWindow.cpp5.4 界面展示1. UDP通信概述 UDP是无连接、…...

【Java】properties 和 yml 的区别

文章目录properties和yml的区别① 定义和定位不同② 语法不同③ yml更好的配置多种数据类型④ yml可以跨语言⑤ 总结properties和yml的区别 这几天刚好看到Spring Boot当中有两种配置文件的方式&#xff0c;但是这两种配置方式有什么区别呢&#xff1f; properties和yml都是S…...

percona软件介绍 、 innobackupex备份与恢复

1. 常用的mysql备份工具 物理备份缺点&#xff1a; 跨平台差。备份时间长、冗余备份、浪费存储空间。 解释如下&#xff1a;如Linux操作系统和Windows操作系统之间&#xff0c;由于文件系统不一样&#xff0c;如Linux操作系统的文件系统是ext4、xfs&#xff0c;Windows操作系统…...

Towards Adversarial Attack on Vision-Language Pre-training Models

摘要虽然视觉-语言预训练模型(VLP)在各种视觉-语言(VL)任务上表现出革命性的改进&#xff0c;但关于其对抗鲁棒性的研究在很大程度上仍未被探索。本文研究了常用VLP模型和VL任务的对抗性攻击。首先&#xff0c;我们分析了不同设置下对抗性攻击的性能。通过研究不同扰动对象和攻…...

2022年最新数据库调查报告:超八成DBA月薪过万,你拖后腿了吗?

数据库管理员属于IT行业高薪职业的一种&#xff0c;近几年关于数据库管理员的薪资统计文章也层出不穷&#xff0c;那么当前&#xff0c;DBA们的薪资究竟到达了怎样的水平呢&#xff1f;墨天轮数据社区发布最新《2022年墨天轮数据库大调查报告》&#xff0c;数据显示超八成DBA月…...

ESP-C3入门10. 创建TCP Client

ESP-C3入门10. 创建TCP Client一、创建 tcp client的一般步骤1. 创建 tcp 套接字2. 配置服务器地址3. 连接服务器4. 发送数据5. 接收数据6. 关闭套接字二、创建tcp_client任务三、示例代码1. tcpClient.h2. tcpClient.c3. main.c一、创建 tcp client的一般步骤 本文示例使用的…...

【Vue】浅谈vue2、vue3响应式原理,vue中数组的响应式,响应式常见问题分析

前言&#xff1a;此处响应式指的是数据响应式变化&#xff0c;而不是页面的响应式布局&#xff0c;页面的响应式布局在我的其他文章中有提到。 一、什么是vue响应式 Vue 最标志性的功能就是其低侵入性的响应式系统。组件状态都是由响应式的 JavaScript 对象组成的。当更改它们…...

股航顶峰先锋一号

{选股} TT:MA(C,30)>MA(C,60) AND MA(C,60)>MA(C,120);{均线多头} DD:C>REF(C,1);{收阳线} QQ:V>REF(V,1);{放量}; TT1:COUNT(L<MA(C,13),5)1; TT2:(C-REF(C,1))/REF(C,1)*100>3; DD1:V>REF(V,1)*2 AND C>REF(C,1); DD2:TT1 AND 0<MA(C,13)AND TT2 …...

MYSQL安装部署--Linux 仓库安装

声明 &#xff1a;# 此次我们安装的 MYSQL 版本是 8.0.32 版本 我们本次安装 MYSQL 总共要介绍 四种方式 # 仓库安装# 本地安装# 容器安装# 源码安装我们本篇介绍的是 仓库安装 仓库安装 下载 MYSQL 安装包 # MYSQL 安装&#xff0c;我们都是基于 MYSQL 官方网站里进行下载~&a…...

NFS服务器搭建

NFS服务器搭建1. NFS简介2. NFS工作原理3. 配置NFS服务端3.1 启动服务3.2 修改配置文件4. 配置NFS客户端1. NFS简介 NFS是Network File System的简写,即网络文件系统. 网络文件系统是FreeBSD支持的文件系统中的一种&#xff0c;也被称为NFS。 NFS允许一个系统在网络上与他人共…...

【数据挖掘实战】——航空公司客户价值分析(K-Means聚类案例)

目录 一、背景和挖掘目标 1、RFM模型缺点分析 2、原始数据情况 3、挖掘目标 二、分析方法与过程 1、初步分析&#xff1a;提出适用航空公司的LRFMC模型 2、总体流程 第一步&#xff1a;数据抽取 第二步&#xff1a;探索性分析 第三步&#xff1a;数据预处理 第四步&…...

AnlogicFPGA-IO引脚约束设置

&#xff08;https://www.eefocus.com/article/472120.html此链接是一篇关于XillinxFPGA的IO的状态分析&#xff0c;希望自己也要能了解到AnLogic的IO状态并有对此问题的分析能力&#xff09; 1、DriveStrength: 驱动强度&#xff0c;即最大能驱动的电流大小&#xff08;见带负…...

Java SSM 笔记(一)重置版

Spring核心技术 **前置课程要求&#xff1a;**请各位小伙伴先完成《JavaWeb》篇、《Java 9-17新特性》篇视频教程之后&#xff0c;再来观看此教程。 **建议&#xff1a;**对Java开发还不是很熟悉的同学&#xff0c;最好先花费半个月到一个月时间大量地去编写小项目&#xff0…...

centos安装java,目录授权

centos安装java (1)查看可安装版本: yum -y list java* 安装&#xff1a;sudo yum -y install java-17-openjdk.x86_64 验证&#xff1a;java –version (2)二进制安装&#xff1a;下载&#xff1a;wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.…...

【大数据】HADOOP-YARN容量调度器多队列配置详解实战

简介 Capacity调度器具有以下的几个特性&#xff1a; 层次化的队列设计&#xff0c;这种层次化的队列设计保证了子队列可以使用父队列设置的全部资源。这样通过层次化的管理&#xff0c;更容易合理分配和限制资源的使用。容量保证&#xff0c;队列上都会设置一个资源的占比&a…...

加密技术在android系统安全中的应用

前言android 系统安全内容总结 1、算法基础 算法基础参照linux的全盘加密与文件系统加密在android中的应用的2、预备知识 android系统安全特性用到加密算法的如下表:...

KNN&K-means从入门到实战

作者&#xff1a;王同学 来源&#xff1a;投稿 编辑&#xff1a;学姐 1. 基本概念 1.1 KNN k近邻法&#xff08;k-nearest neighbor&#xff0c;k-NN&#xff09;是一种基本分类与回归方法。 k近邻法的输入为实例的特征向量对应于特征空间的点&#xff1b;输出为实例的类别&…...

SpringBoot整合RabbitMQ

SpringBoot整合RabbitMQ&#xff0c;生产者 &#xff08;1&#xff09;创建maven项目 &#xff08;2&#xff09;引入依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><versi…...

Hive---安装教程

Hive安装教程 Hive属于Hadoop生态圈&#xff0c;所以Hive必须运行在Hadoop上 文章目录Hive安装教程上传安装包解压并且更名修改 /etc/profile创建hive-site.xml将mysql的jar包放入Hive库中开启刷新配置文件hadoop开启mysql初始化启动hive上传安装包 将安装包上传到/opt/insta…...

MySQL作业四

学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课表&#xff1a;SC (Sno, Cno, Score)…...

云原生安全检测器 Narrows(CNSI)的部署和使用

近日&#xff0c; 云原生安全检测器 Narrows&#xff08;Cloud Native Security Inspector&#xff0c;简称CNSI&#xff09;发布了0.2.0版本。 &#xff08;https://github.com/vmware-tanzu/cloud-native-security-inspector&#xff09; 此项目旨在对K8s集群中的工作负载进…...

【并发编程】【3】Java线程 创建线程与线程运行

并发编程 3.Java线程 本章内容 创建和运行线程 查看线程 线程 API 线程状态 3.1 创建和运行线程 方法一&#xff0c;直接使用 Thread // 创建线程对象 Thread t new Thread() {public void run() {// 要执行的任务} }; // 启动线程 t.start();例如&#xff1a; // 构…...

Ambire 最新消息——2023 年 1 月

大家好&#xff0c;这里是我们在过去几周所做的一切的快速回顾。 发展 整个钱包的交易模拟和余额预测 我们推出了一项真正改变加密钱包 UX 游戏规则的功能&#xff1a;Ambire 现在向用户显示他们的钱包余额将如何更新&#xff0c;甚至在签署交易之前。 这项新功能可以分解为 Am…...

【kubeflow | 镜像源的解决方法——脚本】

20230214 方式一&#xff1a;获取所有镜像列表&#xff0c;自行外网拉取下载 获取KF所需镜像列表脚本 Offical docs for getting all kubeflow images curl https://gist.githubusercontent.com/Jason-CKY/7d7056ce261c6d606585f05218230037/raw/5c27297efdf6424cd9679b9f7…...

function calling convention(函数调用约定)

函数调用约定 函数调用约定,是指当一个函数被调用时,函数的参数会被传递给被调用的函数和返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递和由谁平衡...

errgroup 原理简析

golang.org/x/sync/errgroup errgroup提供了一组并行任务中错误采集的方案。 先看注释 Package errgroup provides synchronization, error propagation, and Context cancelation for groups of goroutines working on subtasks of a common task. Group 结构体 // A Gro…...

Centos7.6 下 Docker 安装

Docker的自动化安装 官方的一键安装方式&#xff1a; curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 国内 daocloud一键安装命令&#xff1a; curl -sSL https://get.daocloud.io/docker | sh Docker手动安装 手动安装Docker分三步&#xff1a;卸…...

C++11--lambda表达式

目录 lambda表达式的概念 lambda表达式语法 lambda表达式的书写格式 捕捉列表 参数列表 mutable 返回值类型 函数体 lambda表达式交换两个数 函数对象与lambda表达式 lambda表达式的概念 lambda表达式是一个匿名函数 它能让代码更加地简洁 提高了代码可读性 首先定义…...

四【Spring框架】

目录一 Spring概述二 .Spring 的体系结构三 Spring的开发环境3.1 配置pom.xml文件四 项目案例&#xff1a;4.1 创建实体类4.2 在pom.xml中引入依赖4.3 配置Spring-config.xml文件4.4 Test✅作者简介&#xff1a;Java-小白后端开发者 &#x1f96d;公认外号&#xff1a;球场上的…...

树与二叉树 总复习

一、树的定义 树是一个有n个&#xff08;n>0&#xff09;结点的有限集合。 如果n0&#xff0c;称为空树&#xff1b; 如果n>0&#xff0c;称为非空树&#xff0c;有且仅有一个特定的称为根Root的结点&#xff08;无直接前驱&#xff09; 如果n>1,除了根节点外&…...

杀手杰夫链接网站代码/公众号推广引流

众包&#xff08;Crowdsourcing&#xff09;是这样一个过程&#xff1a;征求大批社区中的群众去完成一个任务&#xff0c;传统上这种任务由组织从内部选择一拨人来完成&#xff0c;多数是雇员或合同工。众包测试&#xff08;Crowdsourced testing&#xff09;利用众包的有效性和…...

为离职员工做的网站/晋江友情链接是什么意思

这几年微信公众号太火了&#xff0c;无论是传统企业还是互联网公司&#xff0c;基本上都开设了自己的微信公众号&#xff0c;而微信运营之中&#xff0c;最重要的就是写好标题&#xff0c;配好头图&#xff0c;这是做好微信运营的基本。一篇文章的阅读量主要取决于三个因素&…...

重庆建立公司网站/企业网站seo优化

详解Java异常Throwable、Error、Exception、RuntimeException的区别 在Java中&#xff0c;根据错误性质将运行错误分为两类&#xff1a;错误和异常。在Java程序的执行过程中&#xff0c;如果出现了异常事件&#xff0c;就会生成一个异常对象。生成的异常对象将传递Java运行时系…...

快手刷赞网站推广软件/优质的seo快速排名优化

我使用了很多C&#xff0c;我对Java的工作方式感到很困惑&#xff1a;如果我有课的话public class MyClass{private int[] myVariable;...public int[] getVar(){return myVariable;}}然后我想在其他地方使用我的变量&#xff1a;public static void main(String[] args){MyCla…...

用dw建设个人网站视频/广东清远今天疫情实时动态防控

1&#xff0e;设有一个n*m方格的棋盘&#xff08;1≤m,n≤100&#xff09;。 求出该棋盘中包含多少个正方形、多少个长方形&#xff08;不包括正方形&#xff09;。 求内切圆就是求正方形。 //xmin(m,n)-1 //长方形里面数正方形的个数计算公式:m*n(m-1)*(n-1).....(m-x)*(n-x…...

建设电商网站/培训机构不退费最有效方式

linux shell命令下我们可以通过相关命令关闭和重启计算机&#xff0c;下面由学习啦小编为大家搜集整理了linux shell的关机命令是什么的相关知识&#xff0c;希望对大家有帮助!linux shell的关机命令问题描述对于Linux系统而言&#xff0c;许多时候是用于服务器之类的&#xff…...