Qt开发:QUdpSocket的详解
文章目录
- 一、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 地址和端口 45454if (!socket->bind(QHostAddress::AnyIPv4, 45454, QUdpSocket::ShareAddress)) {qDebug() << "Bind failed:" << socket->errorString();return;}// 加入组播地址 239.255.43.21if (!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_OBJECTpublic: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)。
示例代码:
#include <QUdpSocket>
#include <QHostAddress>
#include <QDebug>class MulticastManager : public QObject
{Q_OBJECTpublic: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() 获取错误信息。
使用前提:
必须先通过如下方式加入组播组:
socket->joinMulticastGroup(groupAddress, iface);
然后再用同样的参数退出:
socket->leaveMulticastGroup(groupAddress, iface);
注意:如果加入时没有指定接口,使用的是不带 iface 的重载函数,就不能用这个版本退出。
示例代码:
#include <QUdpSocket>
#include <QNetworkInterface>
#include <QDebug>class MulticastInterfaceManager : public QObject
{Q_OBJECTpublic: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 接收组播数据是通过哪个接口加入的”。
应用场景:
- 当需要发送多播数据,并希望知道使用的是哪块网卡。
- 当之前使用了:
socket->setMulticastInterface(iface);
设置发送使用的接口时,可用 multicastInterface() 读取回来进行校验或日志记录。
示例:查看当前多播发送接口
QUdpSocket socket;// 打印当前默认的发送多播接口(未设置时一般是无效的接口)
QNetworkInterface currentIface = socket.multicastInterface();if (currentIface.isValid()) {qDebug() << "Multicast sending interface:" << currentIface.humanReadableName();
} else {qDebug() << "No multicast interface set for sending.";
}
配套函数:
示例:完整设置和获取发送接口
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() 接收数据。
示例代码:
#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。
通常配套函数:
示例代码:安全读取完整数据报
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 接收。
最小可用例:
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 表示不限。
示例代码:
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+)
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。
最小可用接收示例:
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
// 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
// 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)
#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)。
示例代码:
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() 查看错误原因。
示例:发送数据给远程主机
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;
}
示例:发送广播消息
QUdpSocket socket;
socket.setSocketOption(QAbstractSocket::BroadcastTtlOption, 1); // 设置 TTL 可选QByteArray data = "Broadcast message";
QHostAddress broadcastAddr = QHostAddress::Broadcast; // 或 255.255.255.255socket.writeDatagram(data.data(), data.size(), broadcastAddr, 45454);
示例:发送组播消息
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() 示例
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 示例
QNetworkDatagram datagram;
datagram.setDestination(QHostAddress("192.168.1.100"), 12345);
datagram.setPayload("Hello via QNetworkDatagram");QUdpSocket socket;
socket.writeDatagram(datagram);
示例:广播消息
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() 获取错误信息。
基本用法示例:发送字符串数据
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;
}
示例:广播发送
QUdpSocket socket;
socket.setSocketOption(QAbstractSocket::BroadcastTtlOption, 1); // 可选:设置 TTLQByteArray message = "Broadcast test";
socket.writeDatagram(message, QHostAddress::Broadcast, 45454);
示例:组播发送
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
// 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
// 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)
#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();
}
注意事项:
相关文章:
Qt开发:QUdpSocket的详解
文章目录 一、QUdpSocket 简介二、常用函数的介绍和使用三、接收端完整示例四、发送端完整示例 一、QUdpSocket 简介 在 Qt 中,UDP(User Datagram Protocol,用户数据报协议)是通过 QUdpSocket 类实现的。UDP 是一种无连接的、轻量…...
【android bluetooth 协议分析 01】【HCI 层介绍 9】【ReadLocalSupportedCommands命令介绍】
1. HCI_Read_Local_Supported_Commands 命令介绍 1. 命令介绍(Description) HCI_Read_Local_Supported_Commands 是 HCI 层中非常重要的查询命令。它允许 Host(如 Android 系统中的 Bluetooth stack)获取 Controller(…...
Model 速通系列(一)nanoGPT
这个是新开的一个系列用来手把手复现一些模型工程,之所以开这个系列是因为有人留言说看到一个工程不知道从哪里读起,出于对自身能力的提升与兴趣,故新开了这个系列。由于主要动机是顺一遍代码并提供注释。 该系列第一篇博客是 nanoGPT &…...
星际争霸小程序:用Java实现策略模式的星际大战
在游戏开发的世界里,策略模式是一种非常实用的设计模式,它允许我们在运行时动态地选择算法或行为。今天,我将带你走进一场星际争霸的奇幻之旅,用Java实现一个简单的星际争霸小程序,通过策略模式来模拟不同种族单位的战…...
网络Tips20-007
网络威胁会导致非授权访问、信息泄露、数据被破坏等网络安全事件发生, 其常见的网络威胁包括窃听、拒绝服务、病毒、木马、( 数据完整性破坏 )等, 常见的网络安全防范措施包括访问控制、审计、身份认证、数字签名、( 数据加密 )、 包过滤和检测等。 AE…...
2.微服务-配置
引入springcloud的pom配置 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/></parent> <dependencyManagemen…...
python实现pdf转图片(针对每一页)
from pdf2image import convert_from_path import ospdf_file rC:\Users\\Desktop\拆分\产权证.pdf poppler_path rC:\poppler-24.08.0\Library\bin # 这里改成你自己的路径output_dir rC:\Users\\Desktop\拆分\output_images os.makedirs(output_dir, exist_okTrue)image…...
Python编程从入门到实践 PDF 高清版
各位程序员朋友们,还在为找不到合适的Python学习资料而烦恼吗?还在为晦涩难懂的编程书籍而头疼吗?今天,就给大家带来一份重磅福利——237完整版PDF, 我用网盘分享了「Python编程:从入门到实践__超清版.pdf…...
CVE-2015-3934 Fiyo CMS SQL注入
CVE-2015-3934 Fiyo CMS SQL注入 页面抓登录数据包 构造延时注入语句在user处’%2B(select(0)from(select(sleep(5)))v)%2B’ 存在延时注入,使用脚本即可...
【Pandas】pandas DataFrame mode
Pandas2.2 DataFrame Computations descriptive stats 方法描述DataFrame.abs()用于返回 DataFrame 中每个元素的绝对值DataFrame.all([axis, bool_only, skipna])用于判断 DataFrame 中是否所有元素在指定轴上都为 TrueDataFrame.any(*[, axis, bool_only, skipna])用于判断…...
(思维题、贪心)洛谷 P11232 CSPS2024 超速检测 题解
这一题在 2024 将我击败,但我怎么现在才补题解 …… 题意 原题 思路 对于每一辆车,我们可以算出,其在距离左端点哪段位置会超速 [ l , r ] [l,r] [l,r],那么这辆车会被 l l l 右侧最近的测速仪到 r r r 左侧最近的测速仪检…...
C#:多线程
一.线程常用概念 线程(Thread):操作系统执行程序的最小单位 进程(Process):程序在内存中的运行实例 并发(Concurrency):多个任务交替执行(单核CPU࿰…...
虚拟币制度钱包开发:功能设计与成本全解析
虚拟币制度钱包开发:功能设计与成本全解析 ——从基础架构到合规风控的完整解决方案 一、开发成本:分层定价与关键影响因素 根据2024-2025年行业数据显示,虚拟币钱包App开发成本跨度较大,主要受功能复杂度、技术架构与合规要求三…...
TransmittableThreadLocal实现上下文传递-笔记
1.TransmittableThreadLocal简介 com.alibaba.ttl.TransmittableThreadLocal(简称 TTL)是阿里巴巴开源的一个工具类,旨在解决 ThreadLocal 在线程池中无法传递上下文变量 的问题。它是对 InheritableThreadLocal 的增强,尤其适用…...
应对WEEE 2025:猎板PCB的区块链追溯与高温基材创新
在全球电子产业加速向循环经济转型的背景下,欧盟《绿色新政》与《WEEE指令》对PCB行业提出更高要求。作为行业先行者,猎板PCB(Hunter PCB)以生物降解基材为核心,结合全球合规体系与产业链协同创新,构建从材…...
大陆资产在香港发行RWA的合规路径与核心限制
大陆资产在香港发行RWA的合规路径与核心限制 ——从“双重合规原则”到资产准入边界的全景解读 一、法律框架:双重合规原则的刚性约束 根据香港金管局Ensemble沙盒项目要求,大陆资产在香港发行RWA需遵循“双重合规原则”,即底层资产需同时符…...
爬虫攻防战:从入门到放弃的完整对抗史与实战解决方案
爬虫攻防战:从入门到放弃的完整对抗史与实战解决方案 这张有趣的图片生动描绘了爬虫开发者与反爬工程师之间的"军备竞赛"。作为技术博主,我将基于这张图的各个阶段,深入分析爬虫技术的演进与对应的反制措施,提供一套完整的反爬解决方案,包括技术原理、实施方法…...
Fabric初体验(踩坑笔记)
搭建fabric部署合约学习笔记 环境准备CURl安装docker 参照官网文档实现(2025.05.19)根据前言交代的文章去尝试(失败版)安装fabric-samples安装指定2.2.0版本Fabric二进制文件和配置文件直接手动下载(不建议)…...
区块链blog2_中心化与效率
🌿中心化出现原因 信息/服务分散在各处会浪费时间且不方便使用,由此,把信息/服务集中在一起,便于管理,避免了不必要的效率损失。 即集中资源,使得对信息处理的全过程效率升高。中心化不是网络中产生的&…...
2024年ASOC SCI2区TOP,多机制群优化算法+多风场输电线路巡检中多无人机任务分配与路径规划,深度解析+性能实测
目录 1.摘要2.考虑风场影响的多无人机任务分配3.基于双向蚁群和离散蜜獾算法求解多无人机任务分配问题(BACOHBA)4.考虑风场的多无人机路径规划5.结果展示6.参考文献7.代码获取8.读者交流 1.摘要 随着电力系统规模的不断扩大,复杂环境下的电力线路及设施的巡检与维护…...
智慧赋能光伏运维——无人机巡检+地面监控双链路覆盖,打造光伏电站管理新标杆
一、引言:光伏电站运维的挑战与机遇 在全球能源转型浪潮下,光伏电站作为清洁能源的重要载体,其高效运维管理成为行业核心命题。然而,传统光伏电站运维存在覆盖范围广、设备分散、人工巡检效率低、故障响应慢等痛点。为破解这一难…...
c/c++的opencv开闭操作
OpenCV 中的形态学开运算与闭运算 (C) 在计算机视觉和图像处理领域,形态学操作是用于分析和处理图像形状的一系列非线性操作。OpenCV 作为一个强大的开源计算机视觉库,提供了丰富的形态学转换函数。其中,“开运算”(Opening&…...
Linux利用多线程和线程同步实现一个简单的聊天服务器
1. 概述 本文实现一个基于TCP/IP的简单多人聊天室程序。它包含一个服务器端和一个客户端:服务器能够接收多个客户端的连接,并将任何一个客户端发来的消息广播给所有其他连接的客户端;客户端则可以连接到服务器,发送消息并接收来自…...
无人机遥控器光纤通信模块技术要点!
一、技术要点 1. 长距离低损耗传输 采用单模光纤(如G.654.E光纤),利用光纤的低衰减特性(0.17 dB/km以下),支持10公里以上的远距离通信,突破了传统无线信号因衰减导致的覆盖限制。例如&…...
深入解析OkHttp与Retrofit:Android网络请求的黄金组合
前言 在移动应用开发中,网络请求是连接客户端与服务器的关键桥梁。对于Android开发者而言,OkHttp和Retrofit这对组合已经成为处理网络请求的事实标准。本文将全面剖析这两个框架的设计理念、核心功能、协同关系以及最佳实践,帮助开发者构建高…...
Python操作PDF书签详解 - 添加、修改、提取和删除
目录 简介 使用工具 Python 向 PDF 添加书签 添加书签 添加嵌套书签 Python 修改 PDF 书签 Python 展开或折叠 PDF 书签 Python 提取 PDF 书签 Python 删除 PDF 书签 简介 PDF 书签是 PDF 文件中的导航工具,通常包含一个标题和一个跳转位置(如…...
Spring Boot与Kafka集成实践:从入门到实战
Spring Boot与Kafka集成实践 引言 在现代分布式系统中,消息队列是不可或缺的组件之一。Apache Kafka作为一种高吞吐量的分布式消息系统,广泛应用于日志收集、流处理、事件驱动架构等场景。Spring Boot作为Java生态中最流行的微服务框架,提供…...
luckysheet的使用——17.将表格作为pdf下载到本地
luckysheet源码里面自带有打印按钮,但是功能是无法使用的,所以我把该功能重写了一遍 1.在menuButton.js文件中找到源码打印按钮的触发事件: $("#luckysheet-icon-print").click(function () {}2.使用自己写的挂载方法 window.pr…...
矿井支架LCYVB-6钢丝编织护套连接器介绍
LCYVB-6钢丝编织护套连接器是一种专为矿井支架设计的连接装置,主要用于增强支架的稳定性和安全性。该连接器采用高强度钢丝编织护套,具有优异的抗拉强度和耐磨性,适用于恶劣的矿井环境。 主要特点 高强度钢丝编织护套:采用优质钢…...
git仓库中.git 文件很大,怎么清理掉一部分
查询 .git 文件大小,在 git-bash 里执行(后面有些命令不能执行,也请在 git-bash 里执行) windows11 安装好后右键没有 git bash 命令-CSDN博客 du -sh .git // 592m .git 操作前最好先备份一份,避免推送到远程时出错…...
Qt框架核心组件完全指南:从按钮交互到定时器实现
文章目录 前言一、QAbstractButton 按钮类概述1.1 常用属性1.2 常用信号1.3QButtonGroup 按钮组 二、QComboBox 组合框三、若干与数字相关的组件四、QString 字符串类五、Qt容器类5.1 顺序容器 QList5.2 关联容器 QMap 六、QVariant七、跨平台数据类型7.1 基础数据类型7.2 特殊…...
Axure设计数字乡村可视化大屏:从布局到交互的实战经验分享
乡村治理正从传统模式向“数据驱动”转型。数字乡村可视化大屏作为数据展示的核心载体,不仅能直观呈现乡村发展全貌,还能为决策提供科学依据。本文以Axure为工具,结合实际案例,分享如何从零设计一个功能完备、交互流畅的数字乡村大…...
60天python训练计划----day30
DAY 30 模块和库的导入 知识点回顾: 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑:找到根目录(python解释器的目录和终端的目录不一致) 一.导入官方库 我们复盘下学习python的逻辑,所谓学习pyth…...
HJ3 明明的随机数【牛客网】
文章目录 零、原题链接一、题目描述二、测试用例三、解题思路3.1 快排去重3.2 散列 四、参考代码4.1 快排去重4.2 散列 零、原题链接 HJ3 明明的随机数 一、题目描述 二、测试用例 三、解题思路 3.1 快排去重 基本思路: 先将序列进行快速排序,然后…...
BGP选路
一、拓扑图 二、要求及分析 1.要求 1.使用Preva1策略,确保R4通过R2到达192.168.10.0/24 2、用As Path策略,确保R4通过R3到达192.168.11.0/24 3.配置MED策略,确保R4通过R3到达192.168.12.0/24 4.使用Local Preference策略,确保…...
践行“科学智能”!和鲸打造 AI for Science 专属应用
AI for good, AI for Science. 在传统科研领域,人力与经验的局限始终如影随形。而“AI for Science”正逐渐改变科学研究的模式,以科学数据为基石、以强大算力为支撑,借助人工智能技术,开展计算密集度高且能够实现高效迭代的科学…...
【vs2022的C#窗体项目】打开运行+sql Server改为mysql数据库+发布
1. vs2022打开运行原sql Server的C#窗体项目更改为mysql数据库 1.1. vs2022安装基础模块即可 安装1️⃣vs核心编辑器2️⃣.net桌面开发必选,可选均不安装!!! 为了成功连接mysql数据库,需要安装组件NuGet包管理器 安…...
wpf DataGrid 行选择事件
在WPF中处理DataGrid行选择事件主要有以下几种实现方式: 1.SelectionChanged事件处理 通过直接订阅DataGrid的SelectionChanged事件实现行选择响应: <DataGrid SelectionChanged="DataGridAccurateLocationList_SelectionChanged" .../>后台代码中处理…...
Spring Cloud Seata 深度解析:原理与架构设计
文章目录 前言:为什么我们需要理解分布式事务?一、Seata 核心架构深度拆解1.1 分布式事务核心模型1.2 Seata undo_log 存储结构与版本控制存储结构版本控制核心算法 1.3 Seata 事务模型深度对比与实现原理AT 模式(Auto Transaction࿰…...
从产品展示到工程设计:3DXML 转 STP 的跨流程数据转换技术解析
在数字化设计与制造领域,不同格式的三维模型文件常常需要进行转换,以满足不同软件和工作流程的需求。3DXML 和 STP(STEP AP214/AP242)是工业领域常用的两种三维模型文件格式,3DXML 格式以其轻量化和便于网络传输、可视…...
基于RT-Thread的STM32F4开发第五讲——软件模拟I2C
文章目录 前言一、RT-Thread工程创建二、AT24C02三、函数编写1.I2C_soft.c2.I2C_soft.h3.main.h 四、效果展示五、资源分享总结 前言 本章是基于RT-Thread studio实现软件模拟I2C,开发板是正点原子的STM32F4探索者,使用的RT-Thread驱动是5.1.0࿰…...
pkucpc2025 L:Game on Tree
题意 两个人在一棵无根树上玩游戏,每次可以删掉若干个叶子节点,不能操作的人输。 思路 比赛的时候我去写H Quintuple了,队友貌似在我写的时候把这道题讨论出来了。 后来补题的时候花了大概花了70分钟左右ac这道题。 首先考虑一条链的情况…...
大数据实时分析:ClickHouse、Doris、TiDB 对比分析
随着企业对数据分析实时性、复杂性和多样性的要求越来越高,传统的批处理数仓已经无法满足实时指标看板、流量监控、用户行为分析等场景需求。因此,越来越多的公司开始引入实时分析型数据库系统。 目前,国内外常见的实时分析数据库有: ClickHouse:列式数据库,极致的分析性…...
网络流量分析系统的十大应用场景
在现代企业和组织的IT运维体系中,网络流量分析系统(Network Traffic Analysis, NTA)早已不仅仅是用来查看带宽使用率的“流量计数器”。随着网络环境的复杂化、攻击技术的不断演进,以及对业务连续性要求的提升,网络流量…...
问题 | 代码审查:函数是否包含返回语句
“函数是否包含返回语句”这一问题的核心是:在编程中,函数是否按照设计要求正确使用了 返回语句(如 return、return value),以便向调用者传递结果或控制权。以下是详细解释: 1. 什么是函数的返回语句&#…...
Spring Bean 生命周期中设计模式的应用与解析
Spring Bean 生命周期中使用的设计模式 Spring Bean 的生命周期涉及多个阶段和扩展点,Spring 框架在这一过程中巧妙运用了多种设计模式,以实现强大的功能和灵活性。以下是主要设计模式及其应用场景: 1. 工厂模式(Factory Patter…...
设计模式的原理及深入解析
创建型模式 创建型模式主要关注对象的创建过程,旨在通过不同的方式创建对象,以满足不同的需求。 工厂方法模式 定义:定义一个创建对象的接口,让子类决定实例化哪一个类。 解释:工厂方法模式通过定义一个创建对象的…...
kotlin flow的两种SharingStarted策略的区别
一 两种 SharingStarted 策略的区别: SharingStarted.Eagerly: 立即开始收集上游流,即使没有下游订阅者持续保持活跃状态,直到 ViewModel 被清除优点:响应更快,数据始终保持最新缺点:消耗更多资源&#x…...
BGP综合实验(2)
一、实验需求 1、实验拓扑图 2、实验需求 使用 PreVal 策略,让 R4 经 R2 到达 192.168.10.0/24 。 使用 AS_Path 策略,让 R4 经 R3 到达 192.168.11.0/24 。 配置 MED 策略,让 R4 经 R3 到达 192.168.12.0/24 。 使用 Local Preference 策…...
python使用jsonpath-ng库操作json数据
jsonpath-ng 库的详细使用如下: 一、安装与导入 安装 通过 pip 安装库: pip install jsonpath-ng支持 Python 3.6 及以上版本。 导入核心模块 主要使用 parse 函数和 JSONPath 对象: from jsonpath_ng import parse二、基础查询操作 1. 简单…...