当前位置: 首页 > 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)…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...