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

QT UDP网络编程

一、前言

在网络编程的世界中,UDP(用户数据报协议)是最简单、最快速的传输层协议之一。与TCP不同,UDP不建立连接,不保证数据到达,但正因为这样,它极快、极简单、极轻量

想象一下现实生活中的通信方式:

  • TCP​ 像打电话:先拨号(建立连接),确保对方听到每一句话(可靠传输),最后说再见(断开连接)

  • UDP​ 像对讲机:直接说话,不确认对方是否听到,但可以同时和很多人说话(广播)


二、UDP是什么

UDP(User Datagram Protocol)是一种无连接、不可靠、基于数据报的传输层协议。

UDP核心特性:

  • 无连接:无需建立连接,直接发送

  • 快速:头部开销小(仅8字节),延迟极低

  • 不可靠:不保证数据到达,不保证顺序

  • 支持广播/组播:可以向多个目标同时发送

  • 简单:没有流量控制、拥塞控制等复杂机制

选择UDP 选择TCP
  • 你需要极低延迟(在线游戏、实时音视频)

  • 可以容忍少量丢包(视频会议丢几帧不影响)

  • 需要广播/组播功能(服务发现、实时数据分发)

  • 处理简单查询响应(DNS查询)

  • 大量并发连接(IoT设备、传感器网络)

  • 数据必须完整到达(文件传输、数据库同步)

  • 顺序必须保证(文本传输、远程登录)

  • 需要自动流量控制(避免网络拥塞)

  • 使用标准协议(HTTP、FTP、SMTP等)


三、Qt UDP相关操作

3.1、UDP通信的三种方式

  • 单播
  • // 点对点通信
    udpSocket.writeDatagram(data, QHostAddress("192.168.1.100"), 8888);

 

  • 广播
  • // 发给同一网络的所有主机
    udpSocket.setSocketOption(QAbstractSocket::BroadcastSocketOption, 1);
    udpSocket.writeDatagram(data, QHostAddress::Broadcast, 8888);

 

  • 组播
  • // 发给加入特定组的所有主机
    QHostAddress groupAddress("224.0.0.1");
    udpSocket.joinMulticastGroup(groupAddress);
    udpSocket.writeDatagram(data, groupAddress, 8888);

 

3.2、UDP通讯

#ifndef UDPCONNECT_H
#define UDPCONNECT_H#include <QMainWindow>
#include <QUdpSocket> //UDP发送和监听
#include <QtNetwork>
#include <QList>QT_BEGIN_NAMESPACE
namespace Ui {
class UdpConnect;
}
QT_END_NAMESPACEclass UdpConnect : public QMainWindow
{Q_OBJECTpublic:UdpConnect(QWidget *parent = nullptr);~UdpConnect();private:Ui::UdpConnect *ui;private:void getlocalAddress();//获取本机IP地址QUdpSocket *udpsocket;private slots:void startSevere();//开启服务void closeServe();//关闭服务void sendInformation();//发送消息void broadInformation();//广播消息void socketReadData(); //读取数据
};
#endif // UDPCONNECT_H#include "udpconnect.h"
#include "ui_udpconnect.h"UdpConnect::UdpConnect(QWidget *parent): QMainWindow(parent), ui(new Ui::UdpConnect)
{ui->setupUi(this);getlocalAddress();udpsocket = new QUdpSocket(this);connect(udpsocket, &QUdpSocket::readyRead, this, &UdpConnect::socketReadData);//信号与槽,当udpsocket中存在数据时,自动调用connect(ui->pushButtonStart, &QPushButton::clicked, this, &UdpConnect::startSevere);connect(ui->pushButtonClose, &QPushButton::clicked, this, &UdpConnect::closeServe);connect(ui->pushButtonSend, &QPushButton::clicked, this, &UdpConnect::sendInformation);connect(ui->pushButtonBroad, &QPushButton::clicked, this, &UdpConnect::broadInformation);
}UdpConnect::~UdpConnect()
{delete ui;
}void UdpConnect::getlocalAddress()//获取本机所有接口IP地址
{QList<QNetworkInterface> listinterface = QNetworkInterface::allInterfaces();for(int i = 0; i < listinterface.size(); ++i){QNetworkInterface interface = listinterface[i];if(interface.flags() & QNetworkInterface::IsLoopBack) continue; //跳过回环接口QList<QNetworkAddressEntry> entryies = interface.addressEntries();for(int i = 0; i < entryies.size(); ++i){QHostAddress ip = entryies[i].ip();if(ip.protocol() == QAbstractSocket::IPv4Protocol){ui->comboBox->addItem(ip.toString());}}}
}void UdpConnect::startSevere()
{qint16 port = ui->spinBoxLocalPort->value();QString address = ui->comboBox->currentText().trimmed();QHostAddress hostaddress(address);if(udpsocket->bind(hostaddress, port)){//绑定本机IP和地址,若成功返回Trueui->textEdit->append("********** 绑定成功 **********\n");ui->textEdit->append("********** Port:" + QString::number(udpsocket->localPort()));}else{ui->textEdit->append("********** 绑定失败 **********\n");}
}void UdpConnect::closeServe()
{udpsocket->close();//停止服务ui->textEdit->append("********** 停止服务 **********\n");
}void UdpConnect::sendInformation()
{qint16 targetport = ui->spinBoxTargetPort->value();QString targetaddress = ui->comboBox->currentText().trimmed();QHostAddress tagetaddress(targetaddress);QString strs = ui->lineEditSend->text().trimmed();udpsocket->writeDatagram(strs.toUtf8(), tagetaddress, targetport);//发送数据,包含数据内容,目标IP和端口ui->textEdit->append("你:" + strs);ui->lineEditSend->clear();
}void UdpConnect::broadInformation()
{qint16 targetport = ui->spinBoxTargetPort->value();QString strs = ui->lineEditSend->text().trimmed();udpsocket->writeDatagram(strs.toUtf8(), QHostAddress::Broadcast, targetport);//广播通讯,发送给广播IP对应的端口ui->textEdit->append("你(broadcast):" + strs);ui->lineEditSend->clear();
}void UdpConnect::socketReadData()
{//判读是否有数据报(数据报中包含源于目标IP与端口以及发送的信息)while(udpsocket->hasPendingDatagrams()){QNetworkDatagram datagrams = udpsocket->receiveDatagram();//数据报类型if(datagrams.isValid()){QByteArray data = datagrams.data();//获取数据包信息QHostAddress address = datagrams.senderAddress();//获取数据发送发的IP和Portqint16 port = datagrams.senderPort();ui->textEdit->append("From to:" + address.toString() + ":" + QString::number(port) + '\n');ui->textEdit->append("对方:" + QString::fromUtf8(data) + '\n');}}
}

 

3.3、数据报大小限制

// UDP不保证数据到达,需要应用层处理
class ReliableUDP {void sendWithRetry(const QByteArray &data, int maxRetries = 3) {for (int i = 0; i < maxRetries; i++) {udpSocket.writeDatagram(data, target, port);if (waitForAck(1000)) {  // 等待1秒确认return;  // 收到确认,发送成功}// 超时,重试}qDebug() << "发送失败,达到最大重试次数";}
};

四、TCP、UDP比较

  • UDP:面向数据报(Datagram),每次发送/接收都是一个完整的、有边界的数据包

  • TCP:面向字节流(Stream),数据被视为无边界的数据流,可能被拆分或合并

4.1、TCP通讯模型

  • TCP服务器端:监听与接受连接
  • // 服务器端:QTcpServer负责监听
    QTcpServer *server = new QTcpServer(this);// 1. 监听(绑定IP和端口)
    if (server->listen(QHostAddress::Any, 8888)) {qDebug() << "服务器开始监听端口 8888";// 2. 连接新客户端信号connect(server, &QTcpServer::newConnection, [=]() {// 3. 接受连接,获取通信socketQTcpSocket *clientSocket = server->nextPendingConnection();// 4. 处理客户端数据connect(clientSocket, &QTcpSocket::readyRead, [=]() {QByteArray data = clientSocket->readAll();processClientData(clientSocket, data);});// 5. 处理断开连接connect(clientSocket, &QTcpSocket::disconnected, [=]() {clientSocket->deleteLater();//断开连接信号});});
    }

     

  • TCP客户端:建立连接
  • // 客户端:QTcpSocket负责连接
    QTcpSocket *socket = new QTcpSocket(this);// 1. 连接到服务器
    socket->connectToHost("192.168.1.100", 8888);// 2. 连接成功信号
    connect(socket, &QTcpSocket::connected, [=]() {qDebug() << "已连接到服务器";// 3. 发送数据QString message = "Hello Server!";socket->write(message.toUtf8());//转为字节
    });// 4. 接收数据
    connect(socket, &QTcpSocket::readyRead, [=]() {QByteArray data = socket->readAll();qDebug() << "收到服务器数据:" << data;
    });// 5. 处理断开
    connect(socket, &QTcpSocket::disconnected, [=]() {qDebug() << "与服务器断开连接";
    });

     

4.2、UDP通讯模型

  • UDP绑定与发送
  • // UDP:QUdpSocket负责收发数据报
    QUdpSocket *udpSocket = new QUdpSocket(this);// 1. 绑定到端口(接收数据)
    if (udpSocket->bind(QHostAddress::Any, 8888)) {qDebug() << "UDP绑定到端口 8888";// 2. 接收数据connect(udpSocket, &QUdpSocket::readyRead, [=]() {while (udpSocket->hasPendingDatagrams()) {QNetworkDatagram datagram = udpSocket->receiveDatagram();if (datagram.isValid()) {QByteArray data = datagram.data();QHostAddress sender = datagram.senderAddress();quint16 port = datagram.senderPort();qDebug() << "收到来自" << sender.toString() << ":" << port;qDebug() << "数据:" << data;}}});
    }// 3. 发送数据(无需连接)
    void sendUdpMessage(const QString &targetIp, quint16 targetPort, const QString &message) {QHostAddress target(targetIp);QByteArray data = message.toUtf8();// 直接发送,无需建立连接qint64 sent = udpSocket->writeDatagram(data, target, targetPort);if (sent > 0) {qDebug() << "UDP数据报发送成功";} else {qDebug() << "发送失败:" << udpSocket->errorString();}
    }

 

4.3、TCP流式传输 vs UDP数据报传输

  •  TCP流式传输示例
  • // 发送端:分两次发送
    socket->write("Hello");
    socket->write("World");// 接收端可能的情况:
    // 情况1:一次收到 "HelloWorld"
    // 情况2:先收到 "Hel",再收到 "loWorld"
    // 情况3:分多次收到,每次长度不确定// 这就是TCP的"粘包"问题:消息边界丢失

     

  • UDP数据报传输示例
  • // 发送端:分两次发送
    udpSocket->writeDatagram("Hello", target, port);
    udpSocket->writeDatagram("World", target, port);// 接收端:
    // 第一次接收:一定收到 "Hello"(完整数据报)
    // 第二次接收:一定收到 "World"(完整数据报)
    // 不会出现合并或拆分的情况// UDP保持消息边界:一次发送对应一次接收

     

http://www.jsqmd.com/news/379586/

相关文章:

  • Open-AutoGLM项目实战:在Android设备上构建自动操作与ADB键盘控制
  • 拒绝论文“被收录”风险:2026年最安全的论文降AI率平台深度解析 - 品牌观察员小捷
  • 2026年AIGC痕迹消除与降重实测:为何网易有道“学术猹”能成为行业标杆? - 品牌观察员小捷
  • 《从程序员到CTO沟通说话的力量:技术人有效说服他人的沟通策略与技巧》1
  • 现代C++实现AVL树
  • 西门子数控6FC5373-0AA00-0AA2模块故障代码维修
  • 计算机Java毕设实战-基于web的动物救助网站【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • 《CF708E Student‘s Camp》
  • 【课程设计/毕业设计】基于web的动物救助网站【附源码、数据库、万字文档】
  • Java计算机毕设之基于web的动物救助网站(完整前后端代码+说明文档+LW,调试定制等)
  • Java算法每日一题
  • 如何学习Java AI ?
  • 【毕业设计】基于Springboot的植物健康管理系统设计与实现(源码+文档+远程调试,全bao定制等)
  • flask
  • MAF快速入门(16)用户智能体交互协议AG-UI(上)
  • 详细介绍:HTTP/HTTPS 协议基础详解
  • 【毕业设计】基于web的动物救助网站(源码+文档+远程调试,全bao定制等)
  • 【计算机毕业设计案例】基于web的动物救助网站(程序+文档+讲解+定制)
  • Java毕设选题推荐:基于Springboot的智能养护植物健康管理系统设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 快来看2026市面上混合机供应商口碑排行里的潜力之选,混合机/Z型斗提机/摇摆筛/超声波振动筛,混合机直销厂家推荐排行榜 - 品牌推荐师
  • 决绝
  • 信息论与编码---离散无记忆信道的容量
  • 【计算机毕业设计案例】基于Springboot的植物生长环境植物健康管理系统设计与实现(程序+文档+讲解+定制)
  • 计算机Java毕设实战-基于Springboot的植物健康植物档案管理、智能养护提醒、病虫害管理系统设计与实现【完整源码+LW+部署说明+演示视频,全bao一条龙等】
  • Docker 安装 Python
  • 8:【Git误删】git reflog找回删除文件/ commit
  • 2026年2月分析仪供应商推荐,热门厂商排行抢先看,测厚仪/测定仪/测量仪/分析仪/扭矩仪,分析仪生产商怎么选择 - 品牌推荐师
  • 【课程设计/毕业设计】基于Springboot的植物健康温湿度、光照管理系统设计与实现【附源码、数据库、万字文档】
  • Vue.js前端框架教学之Vue 插槽,以及应用场景
  • 实用指南:AI智能体设计模式系列(八)—— 记忆管理模式