Qt开发:QUdpSocket的详解

文章目录

一、QUdpSocket 简介

在 Qt 中,UDP(User Datagram Protocol,用户数据报协议)是通过 QUdpSocket 类实现的。UDP 是一种无连接的、轻量级的传输层协议,适用于需要快速传输数据且能容忍部分丢包的应用场景,如视频传输、实时通信、游戏等。UDP 消息传送有单播、广播和组播三种方式。

QUdpSocket 是 Qt 提供的用于进行 UDP 网络通信的类,支持发送和接收 UDP 数据报。

常用方法:

  • bind():绑定端口,监听数据。
  • writeDatagram():发送数据。
  • readDatagram():接收数据。
  • hasPendingDatagrams():是否有等待接收的数据。
  • pendingDatagramSize():下一个数据报的大小。

二、常用函数的介绍和使用

2.1 bool QUdpSocket::hasPendingDatagrams() const

功能说明:如果接收缓冲区中有 一个或多个未读取的 UDP 数据报,此函数返回 true。否则,返回 false。

使用示例:

cpp 复制代码
#include <QUdpSocket>
#include <QDebug>

void receiveDatagrams(QUdpSocket *socket)
{
    while (socket->hasPendingDatagrams()) {
        QByteArray datagram;
        datagram.resize(socket->pendingDatagramSize());

        QHostAddress sender;
        quint16 senderPort;

        socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
        qDebug() << "Received datagram from" << sender.toString() << ":" << senderPort;
        qDebug() << "Data:" << QString(datagram);
    }
}

一般写在 readyRead() 槽函数中,例如:

cpp 复制代码
connect(socket, &QUdpSocket::readyRead, this, &YourClass::receiveDatagrams);

注意事项:

  • 应该在 readyRead() 信号触发时调用 hasPendingDatagrams(),而 不是主动轮询,这样可以避免 CPU 占用过高。
  • 每个 readDatagram() 读取的是 一个完整的数据报,因此你需要 循环处理所有的数据报。

2.2 bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress)

功能说明:用于 加入多播组(Multicast Group) 的函数,适用于需要通过 UDP 实现组播通信的场景,比如局域网中的设备广播、视频流同步等。

参数说明:roupAddress:要加入的组播地址,必须是一个合法的多播地址,IPv4 范围通常是 224.0.0.0 到 239.255.255.255。

返回值:返回 true 表示成功加入组播组;返回 false 表示失败,可以通过 error() 和 errorString() 获取错误原因。

在调用 joinMulticastGroup() 之前,必须调用 bind(),且 绑定本地地址和端口。

cpp 复制代码
socket->bind(QHostAddress::AnyIPv4, 45454, QUdpSocket::ShareAddress);
  • ShareAddress 表示允许多个进程/程序绑定相同端口(UDP 组播的典型需求)。
  • ReuseAddressHint 可选,用于跨平台更强的兼容性。

示例代码:加入组播并接收数据

cpp 复制代码
#include <QUdpSocket>
#include <QDebug>

class MulticastReceiver : public QObject
{
    Q_OBJECT
public:
    MulticastReceiver(QObject *parent = nullptr)
    {
        socket = new QUdpSocket(this);

        // 绑定本地任意 IPv4 地址和端口 45454
        if (!socket->bind(QHostAddress::AnyIPv4, 45454, QUdpSocket::ShareAddress)) {
            qDebug() << "Bind failed:" << socket->errorString();
            return;
        }

        // 加入组播地址 239.255.43.21
        if (!socket->joinMulticastGroup(QHostAddress("239.255.43.21"))) {
            qDebug() << "Join multicast group failed:" << socket->errorString();
            return;
        }

        connect(socket, &QUdpSocket::readyRead, this, &MulticastReceiver::readPendingDatagrams);
    }

private slots:
    void readPendingDatagrams()
    {
        while (socket->hasPendingDatagrams()) {
            QByteArray datagram;
            datagram.resize(socket->pendingDatagramSize());

            QHostAddress sender;
            quint16 senderPort;

            socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);

            qDebug() << "Received from" << sender.toString() << ":" << senderPort;
            qDebug() << "Message:" << QString(datagram);
        }
    }

private:
    QUdpSocket *socket;
};

2.3 bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface)

功能说明:用于通过指定网卡接口加入指定组播地址的函数。它是 joinMulticastGroup() 的重载版本,在多网卡环境下尤其重要。

参数说明:

  • groupAddress:要加入的组播地址(必须是合法的 IPv4/IPv6 多播地址)
  • iface 指定的网络接口(例如有线网卡、无线网卡)

返回值:true:成功加入组播组;false:失败,可使用 error() 和 errorString() 获取失败原因。

使用示例:

cpp 复制代码
#include <QUdpSocket>
#include <QNetworkInterface>
#include <QDebug>

class MulticastWithInterface : public QObject
{
    Q_OBJECT

public:
    MulticastWithInterface(QObject *parent = nullptr)
    {
        socket = new QUdpSocket(this);

        // 绑定本地端口,允许地址共享
        if (!socket->bind(QHostAddress::AnyIPv4, 45454, QUdpSocket::ShareAddress)) {
            qDebug() << "Bind failed:" << socket->errorString();
            return;
        }

        // 查找一个支持组播的网络接口
        QNetworkInterface selectedInterface;
        for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
            if (iface.flags().testFlag(QNetworkInterface::IsUp)
                && iface.flags().testFlag(QNetworkInterface::IsRunning)
                && iface.flags().testFlag(QNetworkInterface::CanMulticast)
                && !iface.addressEntries().isEmpty()) {
                selectedInterface = iface;
                break;
            }
        }

        if (!selectedInterface.isValid()) {
            qDebug() << "No valid multicast-capable network interface found.";
            return;
        }

        // 指定网卡加入组播组
        if (!socket->joinMulticastGroup(QHostAddress("239.255.43.21"), selectedInterface)) {
            qDebug() << "Join multicast group failed:" << socket->errorString();
            return;
        }

        connect(socket, &QUdpSocket::readyRead, this, &MulticastWithInterface::readPendingDatagrams);
    }

private slots:
    void readPendingDatagrams()
    {
        while (socket->hasPendingDatagrams()) {
            QByteArray datagram;
            datagram.resize(socket->pendingDatagramSize());
            QHostAddress sender;
            quint16 senderPort;

            socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
            qDebug() << "Received from" << sender.toString() << ":" << senderPort;
            qDebug() << "Data:" << QString(datagram);
        }
    }

private:
    QUdpSocket *socket;
};

2.4 bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress)

功能说明:

  • 将当前 UDP socket 从指定的组播地址中移除。
  • 必须是在之前调用过 joinMulticastGroup(groupAddress) 成功加入的组播组。
  • 如果成功退出,返回 true;否则返回 false,可使用 errorString() 查看原因。

使用前提:

  • socket 必须已经通过 bind() 绑定了端口;
  • 必须已经通过 joinMulticastGroup() 加入了对应的 groupAddress;
  • groupAddress 必须是合法的多播地址(例如 IPv4 地址 239.255.x.x)。

示例代码:

cpp 复制代码
#include <QUdpSocket>
#include <QHostAddress>
#include <QDebug>

class MulticastManager : public QObject
{
    Q_OBJECT

public:
    MulticastManager(QObject *parent = nullptr)
    {
        socket = new QUdpSocket(this);

        if (!socket->bind(QHostAddress::AnyIPv4, 45454, QUdpSocket::ShareAddress)) {
            qDebug() << "Bind failed:" << socket->errorString();
            return;
        }

        groupAddress = QHostAddress("239.255.43.21");

        if (!socket->joinMulticastGroup(groupAddress)) {
            qDebug() << "Join failed:" << socket->errorString();
            return;
        }

        connect(socket, &QUdpSocket::readyRead, this, &MulticastManager::onReadyRead);
    }

    void leaveGroup()
    {
        if (socket->leaveMulticastGroup(groupAddress)) {
            qDebug() << "Successfully left group:" << groupAddress.toString();
        } else {
            qDebug() << "Failed to leave group:" << socket->errorString();
        }
    }

private slots:
    void onReadyRead()
    {
        while (socket->hasPendingDatagrams()) {
            QByteArray datagram;
            datagram.resize(socket->pendingDatagramSize());
            socket->readDatagram(datagram.data(), datagram.size());
            qDebug() << "Received:" << datagram;
        }
    }

private:
    QUdpSocket *socket;
    QHostAddress groupAddress;
};

2.5 bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface)

功能说明:于从指定网卡接口中退出某个组播组的函数,适用于你之前使用带 QNetworkInterface 的 joinMulticastGroup 加入组播组的情况。

参数说明:

  • groupAddress:要退出的组播地址,必须是有效的 IPv4/IPv6 多播地址
  • iface:加入该组播组时所使用的网络接口(网卡)

返回值:

  • true:成功退出组播组;
  • false:失败,可通过 error() 和 errorString() 获取错误信息。

使用前提:

必须先通过如下方式加入组播组:

cpp 复制代码
socket->joinMulticastGroup(groupAddress, iface);

然后再用同样的参数退出:

cpp 复制代码
socket->leaveMulticastGroup(groupAddress, iface);

注意:如果加入时没有指定接口,使用的是不带 iface 的重载函数,就不能用这个版本退出。

示例代码:

cpp 复制代码
#include <QUdpSocket>
#include <QNetworkInterface>
#include <QDebug>

class MulticastInterfaceManager : public QObject
{
    Q_OBJECT

public:
    MulticastInterfaceManager(QObject *parent = nullptr)
    {
        socket = new QUdpSocket(this);

        // 绑定端口
        if (!socket->bind(QHostAddress::AnyIPv4, 45454, QUdpSocket::ShareAddress)) {
            qDebug() << "Bind failed:" << socket->errorString();
            return;
        }

        // 查找可用接口
        for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
            if (iface.flags().testFlag(QNetworkInterface::IsUp)
                && iface.flags().testFlag(QNetworkInterface::IsRunning)
                && iface.flags().testFlag(QNetworkInterface::CanMulticast)
                && !iface.addressEntries().isEmpty()) {
                selectedInterface = iface;
                break;
            }
        }

        groupAddress = QHostAddress("239.255.43.21");

        if (!socket->joinMulticastGroup(groupAddress, selectedInterface)) {
            qDebug() << "Join failed:" << socket->errorString();
            return;
        }

        connect(socket, &QUdpSocket::readyRead, this, &MulticastInterfaceManager::onReadyRead);
    }

    void leaveGroup()
    {
        if (socket->leaveMulticastGroup(groupAddress, selectedInterface)) {
            qDebug() << "Successfully left group on interface:" << selectedInterface.humanReadableName();
        } else {
            qDebug() << "Failed to leave group:" << socket->errorString();
        }
    }

private slots:
    void onReadyRead()
    {
        while (socket->hasPendingDatagrams()) {
            QByteArray datagram;
            datagram.resize(socket->pendingDatagramSize());
            socket->readDatagram(datagram.data(), datagram.size());
            qDebug() << "Received:" << datagram;
        }
    }

private:
    QUdpSocket *socket;
    QHostAddress groupAddress;
    QNetworkInterface selectedInterface;
};

2.6 QNetworkInterface QUdpSocket::multicastInterface() const

函数功能:

该函数用于获取当前 QUdpSocket 对象用于发送多播(Multicast)数据的网络接口(网卡)。

  • 注意:该接口仅影响"发送多播数据"的默认网卡,与接收方的 joinMulticastGroup() 无直接关系。
  • 该函数不会告诉你"当前 socket 接收组播数据是通过哪个接口加入的"。

应用场景:

  • 当需要发送多播数据,并希望知道使用的是哪块网卡。
  • 当之前使用了:
cpp 复制代码
socket->setMulticastInterface(iface);

设置发送使用的接口时,可用 multicastInterface() 读取回来进行校验或日志记录。

示例:查看当前多播发送接口

cpp 复制代码
QUdpSocket socket;

// 打印当前默认的发送多播接口(未设置时一般是无效的接口)
QNetworkInterface currentIface = socket.multicastInterface();

if (currentIface.isValid()) {
    qDebug() << "Multicast sending interface:" << currentIface.humanReadableName();
} else {
    qDebug() << "No multicast interface set for sending.";
}

配套函数:

示例:完整设置和获取发送接口

cpp 复制代码
QUdpSocket socket;

// 查找第一个可用的网卡
for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
    if (iface.flags().testFlag(QNetworkInterface::IsUp) &&
        iface.flags().testFlag(QNetworkInterface::IsRunning) &&
        iface.flags().testFlag(QNetworkInterface::CanMulticast) &&
        !iface.addressEntries().isEmpty()) {

        socket.setMulticastInterface(iface);
        qDebug() << "Set multicast sending interface to:" << iface.humanReadableName();
        break;
    }
}

// 验证设置是否生效
QNetworkInterface activeIface = socket.multicastInterface();
if (activeIface.isValid()) {
    qDebug() << "Current multicast sending interface:" << activeIface.humanReadableName();
}

注意事项:

  • 该设置对发送多播数据生效,不影响接收;
  • 如果不设置,系统可能会根据路由表选择默认网卡;
  • 发送多播时目标地址必须是合法的组播地址(如 239.0.0.1),否则不会走组播流程。

2.7 qint64 QUdpSocket::pendingDatagramSize() const

功能说明:

该函数返回 下一个待接收 UDP 报文的大小(单位:字节),用于提前为接收缓冲区分配合适大小的内存。

常与 hasPendingDatagrams() 搭配使用:

  • hasPendingDatagrams() 判断是否有数据可读;
  • pendingDatagramSize() 获取待接收数据的确切大小;
  • 然后调用 readDatagram() 接收数据。

示例代码:

cpp 复制代码
#include <QUdpSocket>
#include <QDebug>

void receiveDatagram(QUdpSocket *socket)
{
    while (socket->hasPendingDatagrams()) {
        qint64 size = socket->pendingDatagramSize();  // 获取数据大小

        QByteArray buffer;
        buffer.resize(size);  // 根据实际大小分配内存

        QHostAddress sender;
        quint16 senderPort;

        socket->readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);

        qDebug() << "Received" << size << "bytes from" << sender.toString() << ":" << senderPort;
        qDebug() << "Data:" << QString(buffer);
    }
}

注意事项:

  • 如果在没有待处理数据包时调用 pendingDatagramSize(),可能返回 -1;
  • 适用于 UDP 模式,TCP 套接字不适用;
  • 使用固定大小数组如 char buf[1024] 不安全,可能导致截断或溢出;
  • 返回值类型为 qint64,可以应对非常大的数据(尽管 UDP 本身最大为 65507 字节)。

2.8 qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)

功能说明:

该函数用于 从 UDP 套接字中读取一个完整的数据报文(Datagram)。

  • data:指向用户提供的内存缓冲区,用于存放接收到的数据;
  • maxSize:data 缓冲区的最大长度(单位:字节);
  • address(可选):用来接收发送方的 IP 地址;
  • port(可选):用来接收发送方的端口号;
  • 返回值为实际读取的字节数,若出错返回 -1。

通常配套函数:

示例代码:安全读取完整数据报

cpp 复制代码
if (socket->hasPendingDatagrams()) {
    qint64 size = socket->pendingDatagramSize();  // 获取真实数据长度

    QByteArray buffer;
    buffer.resize(size);  // 分配刚好大小的缓冲区

    QHostAddress sender;
    quint16 senderPort;

    qint64 bytesRead = socket->readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);

    if (bytesRead != -1) {
        qDebug() << "Received" << bytesRead << "bytes from" << sender.toString() << ":" << senderPort;
        qDebug() << "Data:" << QString::fromUtf8(buffer);
    } else {
        qWarning() << "Read error:" << socket->errorString();
    }
}

注意事项:

  • 必须为 data 分配足够的空间(否则数据会被截断);
  • readDatagram() 只读一个完整的 UDP 报文,多余的数据会被截断,不会拆包;
  • 可使用 QByteArray buffer(size, 0); 快速创建缓冲区;
  • 不建议手动使用 char buf[1024] 等固定大小数组,可能不安全;
  • 可用于点对点、广播、组播 UDP 接收。

最小可用例:

cpp 复制代码
char buf[2048];
QHostAddress sender;
quint16 port;

qint64 len = socket->readDatagram(buf, sizeof(buf), &sender, &port);

if (len > 0) {
    QByteArray data(buf, len);
    qDebug() << "From" << sender.toString() << ":" << port << "->" << QString(data);
}

2.9 QNetworkDatagram QUdpSocket::receiveDatagram(qint64 maxSize = -1)

功能说明:

该函数用于从 QUdpSocket 接收一个完整的数据报(datagram),并以 QNetworkDatagram 对象 返回,包含以下信息:

相比传统的 readDatagram(),结构更清晰、信息更完整,推荐在新项目中使用。

参数说明:maxSize可选,限制最大读取数据大小,默认 -1 表示不限。

示例代码:

cpp 复制代码
if (socket->hasPendingDatagrams()) {
    QNetworkDatagram datagram = socket->receiveDatagram();  // 默认读取完整数据

    qDebug() << "From:" << datagram.senderAddress().toString() << ":" << datagram.senderPort();
    qDebug() << "To:" << datagram.destinationAddress().toString() << ":" << datagram.destinationPort();
    qDebug() << "Data:" << QString::fromUtf8(datagram.data());
}

QNetworkDatagram 常用成员函数:

搭配 makeReply() 回复数据(Qt 5.10+)

cpp 复制代码
QNetworkDatagram datagram = socket->receiveDatagram();

QByteArray reply = "Hello back!";
QNetworkDatagram replyDatagram = datagram.makeReply(reply);  // 自动填充目标地址和端口
socket->writeDatagram(replyDatagram);

注意事项:

  • receiveDatagram() 只能读取 一个完整的 UDP 报文;
  • 需要 Qt 5.8 及以上版本;
  • 推荐使用 QNetworkDatagram 替代 readDatagram(),尤其在需要获取更多报文信息时;
  • 不支持 Qt TCP 套接字,仅适用于 QUdpSocket。

最小可用接收示例:

cpp 复制代码
connect(socket, &QUdpSocket::readyRead, this, [=]() {
    while (socket->hasPendingDatagrams()) {
        QNetworkDatagram datagram = socket->receiveDatagram();
        qDebug() << "Received from" << datagram.senderAddress().toString() << ":" << datagram.senderPort();
        qDebug() << "Data:" << datagram.data();
    }
});

三、接收端完整示例

下面是一个 完整的 Qt UDP 接收端类,支持:

  • 接收普通 UDP 数据包;
  • 可选接收广播或组播数据;
  • 自动使用 hasPendingDatagrams() + pendingDatagramSize() 接收任意大小数据;
  • 输出发送者 IP/端口 + 数据内容。

示例类:UdpReceiver

cpp 复制代码
// udp_receiver.h
#ifndef UDP_RECEIVER_H
#define UDP_RECEIVER_H

#include <QObject>
#include <QUdpSocket>
#include <QNetworkInterface>

class UdpReceiver : public QObject
{
    Q_OBJECT
public:
    explicit UdpReceiver(QObject *parent = nullptr);

    bool start(quint16 port, bool enableBroadcast = false, const QHostAddress &multicastGroup = QHostAddress());

private slots:
    void onReadyRead();

private:
    QUdpSocket *socket;
};

#endif // UDP_RECEIVER_H
cpp 复制代码
// udp_receiver.cpp
#include "udp_receiver.h"
#include <QDebug>

UdpReceiver::UdpReceiver(QObject *parent) : QObject(parent)
{
    socket = new QUdpSocket(this);
    connect(socket, &QUdpSocket::readyRead, this, &UdpReceiver::onReadyRead);
}

bool UdpReceiver::start(quint16 port, bool enableBroadcast, const QHostAddress &multicastGroup)
{
    // 绑定本地端口,允许共享和广播
    bool success = socket->bind(QHostAddress::AnyIPv4, port,
                                QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);

    if (!success) {
        qWarning() << "Failed to bind socket:" << socket->errorString();
        return false;
    }

    if (enableBroadcast) {
        socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, 65536);
        qDebug() << "Broadcast reception enabled on port" << port;
    }

    if (multicastGroup.isMulticast()) {
        // 自动选一个可用网卡加入组播组
        for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
            if (iface.flags().testFlag(QNetworkInterface::IsUp) &&
                iface.flags().testFlag(QNetworkInterface::IsRunning) &&
                iface.flags().testFlag(QNetworkInterface::CanMulticast) &&
                !iface.addressEntries().isEmpty()) {

                if (!socket->joinMulticastGroup(multicastGroup, iface)) {
                    qWarning() << "Failed to join multicast group:" << socket->errorString();
                } else {
                    qDebug() << "Joined multicast group:" << multicastGroup.toString()
                             << "via interface:" << iface.humanReadableName();
                    break;
                }
            }
        }
    }

    return true;
}

void UdpReceiver::onReadyRead()
{
    while (socket->hasPendingDatagrams()) {
        qint64 size = socket->pendingDatagramSize();
        QByteArray datagram(size, 0);

        QHostAddress sender;
        quint16 senderPort;

        socket->readDatagram(datagram.data(), size, &sender, &senderPort);

        qDebug() << "Received from" << sender.toString() << ":" << senderPort;
        qDebug() << "Size:" << size << "bytes";
        qDebug() << "Data:" << QString::fromUtf8(datagram);
    }
}

用法示例(main.cpp)

cpp 复制代码
#include <QCoreApplication>
#include "udp_receiver.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    UdpReceiver receiver;

    // 启动监听:监听 45454 端口,支持广播,加入 239.255.43.21 的组播
    receiver.start(45454,
                   true,
                   QHostAddress("239.255.43.21"));  // 留空表示不加入组播

    return a.exec();
}

注意:

  • 若仅需接收普通 UDP,可不启用广播和组播;
  • 若接收广播:确保绑定时设置了 ShareAddress | ReuseAddressHint,并在发送端启用 Broadcast;
  • 若接收组播:需要加入合法的组播地址,且网卡必须支持组播;

2.9.1 void QUdpSocket::setMulticastInterface(const QNetworkInterface &iface)

功能说明:该函数用于设置发送组播(Multicast)数据时所使用的网卡(网络接口)。当的机器有多个网卡(如:有线网卡、无线网卡、虚拟网卡)时,调用此函数可以显式指定组播数据从哪一个网卡发送。仅影响发送组播,对接收无影响。

常见使用场景:

  • 多网卡设备:发送组播时必须指定正确的网卡;
  • 虚拟机环境:避免组播走到不通的虚拟网卡;
  • 嵌入式设备:发送组播到特定网络接口(如 eth0, wlan0)。

示例代码:

cpp 复制代码
QUdpSocket socket;

// 构造组播地址
QHostAddress multicastAddress("239.255.43.21");
quint16 port = 45454;
QByteArray data = "Hello multicast";

// 遍历网卡选择一个支持组播的接口
for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
    if (iface.flags().testFlag(QNetworkInterface::IsUp) &&
        iface.flags().testFlag(QNetworkInterface::IsRunning) &&
        iface.flags().testFlag(QNetworkInterface::CanMulticast)) {

        socket.setMulticastInterface(iface);
        qDebug() << "Sending multicast via interface:" << iface.humanReadableName();
        break;
    }
}

// 发送组播
socket.writeDatagram(data, multicastAddress, port);

注意事项:

*2.9.2 qint64 writeDatagram(const char data, qint64 size, const QHostAddress &address, quint16 port)

功能说明:此函数用于通过 UDP 套接字发送一个数据报(Datagram)到指定的地址和端口。它是 UDP 发送数据的最常用方法之一。

参数说明:

返回值:

  • 成功:返回实际发送的字节数;
  • 失败:返回 -1,并可通过 errorString() 查看错误原因。

示例:发送数据给远程主机

cpp 复制代码
QUdpSocket socket;

QByteArray data = "Hello, UDP!";
QHostAddress targetAddress("192.168.1.100");
quint16 targetPort = 12345;

qint64 bytesSent = socket.writeDatagram(data.data(), data.size(), targetAddress, targetPort);

if (bytesSent == -1) {
    qWarning() << "Failed to send datagram:" << socket.errorString();
} else {
    qDebug() << "Sent" << bytesSent << "bytes to" << targetAddress.toString() << ":" << targetPort;
}

示例:发送广播消息

cpp 复制代码
QUdpSocket socket;
socket.setSocketOption(QAbstractSocket::BroadcastTtlOption, 1);  // 设置 TTL 可选

QByteArray data = "Broadcast message";
QHostAddress broadcastAddr = QHostAddress::Broadcast;  // 或 255.255.255.255

socket.writeDatagram(data.data(), data.size(), broadcastAddr, 45454);

示例:发送组播消息

cpp 复制代码
QUdpSocket socket;
QHostAddress multicastAddr("239.255.0.1");
quint16 port = 45454;

// 可选:绑定本地端口或设置发送网卡
socket.bind(QHostAddress::AnyIPv4, 0);  // 临时端口
// socket.setMulticastInterface(yourInterface);

QByteArray data = "Multicast message";
socket.writeDatagram(data.data(), data.size(), multicastAddr, port);

2.9.2 qint64 QUdpSocket::writeDatagram(const QNetworkDatagram &datagram)

功能说明:该函数用于通过 QUdpSocket 发送一个完整的 QNetworkDatagram 数据报。它是 writeDatagram(const char*, qint64, QHostAddress, quint16) 的增强版本,使用面向对象的 QNetworkDatagram 更安全、结构更清晰,适合现代 Qt 项目。

主要用途:

  • 用于发送携带完整信息(如:数据、目标地址、端口)的 QNetworkDatagram;
  • 支持构造用于 组播、广播、应答(reply) 的复杂数据报;
  • 与 receiveDatagram() 相互配合,支持一键应答。

参数说明:包含数据、目标地址、目标端口等信息的 UDP 数据报

搭配 makeReply() 示例

cpp 复制代码
connect(&socket, &QUdpSocket::readyRead, this, [&]() {
    while (socket.hasPendingDatagrams()) {
        QNetworkDatagram d = socket.receiveDatagram();
        qDebug() << "Received from" << d.senderAddress() << ":" << d.senderPort()
                 << "Data:" << d.data();

        // 构造回复报文(自动使用对方地址和端口)
        QNetworkDatagram reply = d.makeReply("Ack from server");
        socket.writeDatagram(reply);
    }
});

构造并发送 QNetworkDatagram 示例

cpp 复制代码
QNetworkDatagram datagram;
datagram.setDestination(QHostAddress("192.168.1.100"), 12345);
datagram.setPayload("Hello via QNetworkDatagram");

QUdpSocket socket;
socket.writeDatagram(datagram);

示例:广播消息

cpp 复制代码
QNetworkDatagram broadcast;
broadcast.setDestination(QHostAddress::Broadcast, 45454);
broadcast.setPayload("Broadcast test");

socket.writeDatagram(broadcast);

2.9.3 qint64 QUdpSocket::writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)

功能说明:该函数通过 UDP 向指定的主机地址和端口 发送一个 QByteArray 数据报。这是 Qt 中发送 UDP 数据的常用接口之一,适用于简洁发送操作。

参数说明:

返回值:

  • 成功:返回写入的字节数;
  • 失败:返回 -1,可通过 errorString() 获取错误信息。

基本用法示例:发送字符串数据

cpp 复制代码
QUdpSocket socket;

QByteArray data = "Hello, UDP!";
QHostAddress address("192.168.1.100");
quint16 port = 12345;

qint64 bytes = socket.writeDatagram(data, address, port);

if (bytes == -1) {
    qWarning() << "UDP send failed:" << socket.errorString();
} else {
    qDebug() << "Sent" << bytes << "bytes to" << address.toString() << ":" << port;
}

示例:广播发送

cpp 复制代码
QUdpSocket socket;
socket.setSocketOption(QAbstractSocket::BroadcastTtlOption, 1);  // 可选:设置 TTL

QByteArray message = "Broadcast test";
socket.writeDatagram(message, QHostAddress::Broadcast, 45454);

示例:组播发送

cpp 复制代码
QUdpSocket socket;
QByteArray message = "Multicast hello";
QHostAddress multicastAddress("239.255.0.1");  // IPv4 多播地址

// 绑定临时端口(可选)
socket.bind(QHostAddress::AnyIPv4, 0);

// 指定发送接口(可选)
// socket.setMulticastInterface(interface);

socket.writeDatagram(message, multicastAddress, 12345);

四、发送端完整示例

以下是配套的 Qt UDP 发送端类,支持:

  • 普通 UDP 数据发送;
  • 广播发送(255.255.255.255 或网段广播地址);
  • 组播发送(如 239.x.x.x),并可指定发送网卡(setMulticastInterface);

类定义:UdpSender

cpp 复制代码
// udp_sender.h
#ifndef UDP_SENDER_H
#define UDP_SENDER_H

#include <QObject>
#include <QUdpSocket>
#include <QHostAddress>
#include <QNetworkInterface>

class UdpSender : public QObject
{
    Q_OBJECT
public:
    explicit UdpSender(QObject *parent = nullptr);

    void sendTo(const QHostAddress &targetAddress,
                quint16 targetPort,
                const QByteArray &data,
                bool isMulticast = false,
                const QNetworkInterface &iface = QNetworkInterface());

private:
    QUdpSocket *socket;
};

#endif // UDP_SENDER_H
cpp 复制代码
// udp_sender.cpp
#include "udp_sender.h"
#include <QDebug>

UdpSender::UdpSender(QObject *parent) : QObject(parent)
{
    socket = new QUdpSocket(this);
}

void UdpSender::sendTo(const QHostAddress &targetAddress,
                       quint16 targetPort,
                       const QByteArray &data,
                       bool isMulticast,
                       const QNetworkInterface &iface)
{
    if (isMulticast) {
        // 如果是组播,设置发送所用的网卡接口
        if (iface.isValid()) {
            socket->setMulticastInterface(iface);
            qDebug() << "Set multicast interface to:" << iface.humanReadableName();
        }
    }

    qint64 bytes = socket->writeDatagram(data, targetAddress, targetPort);

    if (bytes == -1) {
        qWarning() << "Send failed:" << socket->errorString();
    } else {
        qDebug() << "Sent" << bytes << "bytes to" << targetAddress.toString() << ":" << targetPort;
    }
}

用法示例(main.cpp)

cpp 复制代码
#include <QCoreApplication>
#include "udp_sender.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    UdpSender sender;

    QString message = "Hello from sender";
    QByteArray data = message.toUtf8();

    quint16 port = 45454;

    // 1️⃣ 普通 UDP 发送
    sender.sendTo(QHostAddress("127.0.0.1"), port, data);

    // 2️⃣ 广播发送
    sender.sendTo(QHostAddress("255.255.255.255"), port, data);  // 必须绑定时允许广播

    // 3️⃣ 组播发送
    QNetworkInterface multicastIface;
    for (const QNetworkInterface &iface : QNetworkInterface::allInterfaces()) {
        if (iface.flags().testFlag(QNetworkInterface::IsUp) &&
            iface.flags().testFlag(QNetworkInterface::IsRunning) &&
            iface.flags().testFlag(QNetworkInterface::CanMulticast)) {
            multicastIface = iface;
            break;
        }
    }

    sender.sendTo(QHostAddress("239.255.43.21"), port, data, true, multicastIface);

    return a.exec();
}

注意事项:

相关推荐
feiyangqingyun37 分钟前
Qt音视频开发过程中一个疑难杂症的解决方法/ffmpeg中采集本地音频设备无法触发超时回调
qt·ffmpeg·音视频
yxc_inspire1 小时前
基于Qt的app开发第九天
c++·qt·app
半青年2 小时前
Qt读取Excel文件的技术实现与最佳实践
c语言·c++·python·qt·c#·excel
夜松云3 小时前
Qt框架核心组件完全指南:从按钮交互到定时器实现
数据库·qt·交互·信号与槽·ui组件·容器类·定时器机制
煮雪品茶3 小时前
Windows 下 Qt 项目配置 FFmpeg 简明指南
qt·ffmpeg
__ocean4 小时前
编译Qt5.15.16并启用pdf模块
开发语言·qt·pdf
m0_5557629013 小时前
Qt缓动曲线详解
开发语言·qt
若水晴空初如梦18 小时前
QT聊天项目DAY11
开发语言·qt
mahuifa1 天前
ubuntu18.04编译qt5.14.2源码
开发语言·数据库·qt