[QT] UDP communication QUdpSocket (unicast, broadcast, multicast)

1. Overview of UDP communication

UDP is a connectionless, unreliable, and datagram-oriented protocol that can be applied to occasions that do not require high reliability. Unlike TCP communication, UDP communication does not need to establish a persistent socket connection in advance, and UDP needs to specify the destination address and port every time it sends a datagram.

QUdpSocket transmits data in datagrams, not in a continuous stream of data. Use the function to send the datagram QUdpSocket::writeDatagram(), the length of the datagram is generally less than 512 bytes, and each datagram contains information such as the IP address and port of the sender and receiver.
To receive UDP data, you first need to QUdpSocket::bind()bind a port. After binding the port, the state of the socket will change to the bound state "BoundState". When a datagram comes in, QudpSocket will automatically emit readyRead()a signal and use it in its slot function QUdpSocket::readDatagram()to read data. abort()In order to unbind, the socket status becomes "UnconnectedState" after unbinding.

2. Three modes of UDP message transmission

Unicast mode (unicast): A UDP client sends a datagram to another UDP client at a specified address and port, which is a one-to-one data transmission.

Broadcast mode (broadcast): A datagram sent by a UDP client can be received by all other UDP clients within the same network range. QUdpSocket supports IPv4 broadcast. The address of the receiving end needs to be specified in the datagram QHostAddress::Broadcast, and the general broadcast address is 255.255.255.255.

Multicast mode (multicast): The UDP client joins another multicast group with a multicast IP address, and all members who join the multicast can receive the datagrams sent by members to the multicast address, similar to the QQ group function . QUdpSocket::joinMulticastGroup()The function implements the function of joining a multicast group.

In unicast, broadcast, and multicast modes, UDP programs are all peer-to-peer, unlike TCP communication that is divided into client and server.
TCP communication only has unicast mode. Although UDP communication cannot guarantee the accuracy of data transmission, it has flexibility. General instant messaging software is based on UDP communication.

3. Interface functions of the QUdpSocket class

bool bind(quint16 port = 0)Bind a port for UDP communication

qint64 writeDatagram(QByteArray& datagram, QHostAddress& host, quint16 port)Send a datagram to the UDP client at the destination address and port, and return the number of bytes successfully sent. The length of the datagram generally does not exceed 512 bytes.

bool hasPendingDatagrams()Returns true when at least one datagram needs to be read

qint64 pendingDatagramSize()Returns the size of the first datagram to be read

qint64 readDatagram(char* data, qint64 maxSize)Read a datagram, returning the number of bytes successfully read

qint64 readDatagram(char* data, qint64 maxSize, QHostAddress* address, quint16* port)Read a datagram, returning the number of bytes successfully read. The sender's host address and port are stored in *address and *port (unless the pointer is 0)

bool joinMulticastGroup(QHostAddress& groupAddress)join a multicast group

bool leaveMulticastGroup(QHostAddress& groupAddress)leave a multicast group

void abort()Terminates the current connection and resets the socket. Usually written in the destructor. Unlike disconnectFromHost(), this function closes the socket immediately, discarding any pending data written to the buffer.

4. UDP unicast and broadcast code example

4.1 Test Description

This example realizes unicast and broadcast of UDP communication. Both instances can run on the same computer or on different computers.
The two instances here are running on the same computer. It should be noted that when running on the same computer, the two instances need to be bound to different ports. For example, instance A is bound to port 1600, and instance B is bound to port 3200. When instance A sends a datagram to instance B, it needs to specify the port of instance B so that instance B can receive the data.
If the two instances are running on different computers, the ports can be the same, because the IP addresses are different, which will not cause conflicts during binding. General UDP communication programs are run on different computers, and a fixed port is agreed as the communication port.

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_OBJECT

public:
    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);

    // connect
    connect(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();  //目标IP
    QHostAddress 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 Interface display

insert image description here

5. UDP multicast code example

5.1 Characteristics of multicast

The destination address of the multicast message uses a class D IP address. Regarding the multicast IP address, there are the following conventions:

  1. 224.0.0.0 ~ 224.0.0.255 are reserved multicast addresses (permanent group addresses), the address 224.0.0.0 is reserved and not allocated, and other addresses are used by routing protocols.
  2. 224.0.1.0 ~ 224.0.1.255 are public multicast addresses that can be used on the Internet.
  3. 224.0.2.0 ~ 238.255.255.255 are multicast addresses (temporary group addresses) available to users and are valid in the entire network.
  4. 239.0.0.0 ~ 239.255.255.255 are local management multicast addresses, which are only valid within a specific local range. Therefore, if you test the UDP multicast function in your home or office LAN, you can use these IPs.

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_OBJECT

public:
    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);

    // connect
    connect(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的情况,所以就需要写default
    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::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 Interface display

insert image description here

Guess you like

Origin blog.csdn.net/WL0616/article/details/129050373