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。

使用示例:

#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() 槽函数中,例如:

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(),且 绑定本地地址和端口。

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

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

#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() 获取失败原因。

使用示例:

#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() 绑定了端口;
  • 必须已经通
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值