当前位置: 首页 > news >正文

Qt UDP 网络编程详解

一、UDP 协议核心特性
  1. 无连接协议:通信前无需建立连接

  2. 面向数据报:每次发送/接收独立的数据包

  3. 轻量高效:头部开销小(仅8字节)

  4. 不可靠传输

    • 不保证数据包顺序

    • 不保证数据包到达

    • 无自动重传机制

  5. 适用场景

    • 实时音视频传输

    • DNS 查询

    • 网络状态广播

    • 游戏状态同步

二、Qt UDP 核心类

类名

功能说明

QUdpSocket

UDP 数据报套接字

QHostAddress

IP 地址封装

QNetworkDatagram

数据报容器(Qt 5.8+)

三、UDP 发送端实现
#include <QUdpSocket>
#include <QDebug>class UdpSender : public QObject {Q_OBJECT
public:explicit UdpSender(QObject *parent = nullptr) : QObject(parent), udpSocket(new QUdpSocket(this)) {// 设置广播选项udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);}// 发送广播消息void broadcastMessage(const QString &message) {QByteArray datagram = message.toUtf8();// 发送到广播地址qint64 bytesSent = udpSocket->writeDatagram(datagram,QHostAddress::Broadcast,  // 255.255.255.25545454);if (bytesSent == -1) {qWarning() << "Failed to send datagram:" << udpSocket->errorString();} else {qInfo() << "Broadcasted message:" << message << "to" << QHostAddress::Broadcast.toString();}}// 发送到指定地址void sendTo(const QString &message, const QHostAddress &address, quint16 port) {QByteArray datagram = message.toUtf8();udpSocket->writeDatagram(datagram, address, port);}private:QUdpSocket *udpSocket;
};
四、UDP 接收端实现
#include <QUdpSocket>
#include <QNetworkDatagram>class UdpReceiver : public QObject {Q_OBJECT
public:explicit UdpReceiver(QObject *parent = nullptr) : QObject(parent), udpSocket(new QUdpSocket(this)) {// 绑定到端口接收数据if (!udpSocket->bind(45454, QUdpSocket::ShareAddress)) {qCritical() << "Bind failed:" << udpSocket->errorString();return;}qInfo() << "Listening on UDP port 45454...";// 连接数据到达信号connect(udpSocket, &QUdpSocket::readyRead, this, &UdpReceiver::processPendingDatagrams);}private slots:void processPendingDatagrams() {while (udpSocket->hasPendingDatagrams()) {// 使用QNetworkDatagram获取元数据(Qt 5.8+)QNetworkDatagram datagram = udpSocket->receiveDatagram();if (!datagram.isValid()) continue;QByteArray data = datagram.data();QHostAddress senderAddress = datagram.senderAddress();quint16 senderPort = datagram.senderPort();qInfo() << "Received from" << senderAddress.toString() << ":" << senderPort << "=>" << data;// 示例:处理特定命令if (data.startsWith("PING")) {sendResponse(senderAddress, senderPort);}}}void sendResponse(const QHostAddress &addr, quint16 port) {udpSocket->writeDatagram("PONG", addr, port);}private:QUdpSocket *udpSocket;
};
五、关键技术解析
  1. 绑定模式选项

// 常用绑定选项
udpSocket->bind(port, QUdpSocket::ShareAddress);      // 允许多个套接字绑定同一端口
udpSocket->bind(port, QUdpSocket::ReuseAddressHint);  // 地址重用
udpSocket->bind(QHostAddress::AnyIPv4);               // 监听所有IPv4接口
  1. 数据报读写方法对比

// 传统方法(Qt 4/5)
char buffer[1024];
qint64 size = udpSocket->pendingDatagramSize();
udpSocket->readDatagram(buffer, size, &senderAddr, &senderPort);// 现代方法(Qt 5.8+)
QNetworkDatagram datagram = udpSocket->receiveDatagram();
if (datagram.isValid()) {QByteArray data = datagram.data();// 使用datagram.senderAddress()等获取元数据
}
  1. 广播与组播

// IPv4 广播
udpSocket->writeDatagram(data, QHostAddress::Broadcast, port);// IPv6 组播
QHostAddress groupAddress("FF02::1"); // 所有节点组播地址
udpSocket->joinMulticastGroup(groupAddress);
udpSocket->writeDatagram(data, groupAddress, port);
六、高级应用场景
  1. 实现服务发现协议

// 服务端广播服务信息
void ServiceDiscoverer::broadcastService() {QJsonObject serviceInfo{{"name", "File Server"},{"type", "_fileserver._tcp"},{"port", 8080},{"ip", getLocalIP()}};QByteArray datagram = QJsonDocument(serviceInfo).toJson();udpSocket->writeDatagram(datagram, QHostAddress::Broadcast, 5353);
}// 客户端监听服务
void ServiceBrowser::startDiscovery() {udpSocket->bind(5353, QUdpSocket::ShareAddress);connect(udpSocket, &QUdpSocket::readyRead, [this]() {while (udpSocket->hasPendingDatagrams()) {QNetworkDatagram datagram = udpSocket->receiveDatagram();QJsonObject service = QJsonDocument::fromJson(datagram.data()).object();emit serviceDiscovered(service);}});
}
  1. 实现简单可靠传输

// 带序列号的数据报
struct ReliableDatagram {quint32 sequence;QByteArray payload;
};// 发送端
void sendReliable(const QByteArray &data) {static quint32 seq = 0;ReliableDatagram dg{++seq, data};QByteArray datagram;QDataStream out(&datagram, QIODevice::WriteOnly);out << dg.sequence << dg.payload;udpSocket->writeDatagram(datagram, receiverAddr, port);// 启动重传定时器QTimer::singleShot(200, [this, seq, datagram]() {if (!acknowledged.contains(seq)) {udpSocket->writeDatagram(datagram, receiverAddr, port); // 重传}});
}// 接收端
void processDatagram() {QNetworkDatagram dg = udpSocket->receiveDatagram();ReliableDatagram packet;QDataStream in(dg.data());in >> packet.sequence >> packet.payload;// 发送ACKQByteArray ack;QDataStream ackOut(&ack, QIODevice::WriteOnly);ackOut << packet.sequence;udpSocket->writeDatagram(ack, dg.senderAddress(), dg.senderPort());// 处理数据if (packet.sequence > lastSequence) {processPayload(packet.payload);lastSequence = packet.sequence;}
}
七、调试与优化技巧
  1. 网络诊断命令

# 查看UDP端口监听
netstat -anu# 测试UDP连通性
nc -u <host> <port>
  1. 性能优化

// 增大发送缓冲区
udpSocket->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, 1024 * 1024);// 禁用拥塞控制 (实时应用)
udpSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);// 使用原始套接字 (需要权限)
if (udpSocket->bind(QHostAddress::Any, port, QUdpSocket::DontShareAddress)) {udpSocket->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, 0);
}
  1. 错误处理

connect(udpSocket, &QAbstractSocket::errorOccurred, [](QAbstractSocket::SocketError error) {switch(error) {case QAbstractSocket::AddressInUseError:qCritical() << "Port already in use";break;case QAbstractSocket::DatagramTooLargeError:qWarning() << "Datagram exceeds MTU size";break;case QAbstractSocket::NetworkError:qWarning() << "Network error occurred";break;default:qWarning() << "UDP error:" << error;}
});
八、完整示例:局域网设备发现
// 设备发现广播器
class DeviceBroadcaster : public QObject {
public:DeviceBroadcaster() {connect(&timer, &QTimer::timeout, this, &DeviceBroadcaster::broadcast);timer.start(5000); // 每5秒广播一次}private:void broadcast() {QJsonObject deviceInfo{{"name", QSysInfo::machineHostName()},{"os", QSysInfo::prettyProductName()},{"ip", getLocalIP()},{"timestamp", QDateTime::currentSecsSinceEpoch()}};QByteArray datagram = QJsonDocument(deviceInfo).toJson();QUdpSocket socket;socket.writeDatagram(datagram, QHostAddress::Broadcast, 37020);}QString getLocalIP() {foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) {if (address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress::LocalHost) {return address.toString();}}return "127.0.0.1";}QTimer timer;
};// 设备发现监听器
class DeviceListener : public QObject {
public:DeviceListener() {udpSocket.bind(37020, QUdpSocket::ShareAddress);connect(&udpSocket, &QUdpSocket::readyRead, this, &DeviceListener::processDatagrams);}signals:void deviceFound(const QJsonObject &deviceInfo);private:void processDatagrams() {while (udpSocket.hasPendingDatagrams()) {QNetworkDatagram datagram = udpSocket.receiveDatagram();QJsonObject device = QJsonDocument::fromJson(datagram.data()).object();// 过滤过期的广播qint64 now = QDateTime::currentSecsSinceEpoch();if (now - device["timestamp"].toInt() < 10) {emit deviceFound(device);}}}QUdpSocket udpSocket;
};

最佳实践:对于需要可靠传输的场景,可在应用层实现ACK机制和序列号;对于实时流媒体,可结合RTP协议;在公网环境中使用时,需注意NAT穿透问题(可借助STUN/TURN服务器)。


文章转载自:

http://8gmam5Cw.bpmdg.cn
http://HwYix0Bi.bpmdg.cn
http://Qkg1raPG.bpmdg.cn
http://P8wnrMsI.bpmdg.cn
http://btypvKvr.bpmdg.cn
http://FgbU8P2D.bpmdg.cn
http://uIVJKyIE.bpmdg.cn
http://ZP584JAM.bpmdg.cn
http://rSzk4f5Q.bpmdg.cn
http://8HNySnhk.bpmdg.cn
http://byrJTyV4.bpmdg.cn
http://IEZq5y9D.bpmdg.cn
http://GDbTwcZJ.bpmdg.cn
http://F5R6VjIn.bpmdg.cn
http://Y8a8pukp.bpmdg.cn
http://qaqQOSRg.bpmdg.cn
http://lrPEkjVU.bpmdg.cn
http://noY53aiG.bpmdg.cn
http://dsAlG5JH.bpmdg.cn
http://eAfZ61SK.bpmdg.cn
http://97l7grXr.bpmdg.cn
http://CqXP6MYm.bpmdg.cn
http://RBnDl31w.bpmdg.cn
http://E4oyGy6x.bpmdg.cn
http://oOQdSB1I.bpmdg.cn
http://JQ3mxxfd.bpmdg.cn
http://n1AwcM4D.bpmdg.cn
http://Y1h7ghP0.bpmdg.cn
http://CZfasxN7.bpmdg.cn
http://UpOfnjqG.bpmdg.cn
http://www.dtcms.com/a/372738.html

相关文章:

  • CUPP针对性字典安全防范
  • 用nasm汇编器汇编不同位数格式的ELF
  • odoo打印pdf速度慢问题
  • 京东商品评论 API(JSON 数据返回)核心解析
  • SpringMVC(二)
  • 开始理解大型语言模型(LLM)所需的数学基础
  • 搭论文大纲逻辑乱易跑题?AI 3 步梳理框架,自动串逻辑链
  • C#SqlSugar的简单使用
  • 【军事类】军舰识别检测数据集:3400+图像,4类,yolo标注
  • 基于若依框架Vue+TS导出PDF文件的方法
  • ArcGIS学习-18 实战-降雨量空间分布插值分析
  • OpenCV 银行卡号识别
  • CentOS 8重启后网卡不见了解决办法
  • Nginx反向代理和负载均衡详解及使用Nginx和tomcat共同实现动静分离配置
  • UDP的使用
  • WGAI项目图像视频语音识别功能
  • 9.3深度循环神经网络
  • 【嵌入式硬件实例】-555定时器实现自动晚灯(220V)
  • Linux Shell | set、env、export 用法区别
  • 浅聊一下微服务的服务保护
  • Nginx 实战系列(五)—— Nginx流量监控:从stub_status到nginx-module-vts的进阶指南
  • 34. 什么是反射
  • YOLO11 改进、魔改|通道自注意力卷积块CSA-ConvBlock,实现 “轻量化特征增强”
  • 优先搜索(DFS)实战
  • 计算机视觉opencv----银行卡号码识别
  • 第六章、从transformer到nlp大模型:编码器-解码器模型 (Encoder-Decoder)
  • pymodbus启动一个简单的modbus tcp server
  • 【NowCoder】牛客周赛 Round 108 EF (背包问题 | SOSDP)
  • 【ARMday02】
  • OFDR设备开机到出图的5个关键操作步骤