QT的UDP

发布于:2025-08-05 ⋅ 阅读:(16) ⋅ 点赞:(0)

定义
UDP是一种无连接的传输层协议。它位于TCP/IP协议栈的传输层,为应用层提供了一种不需要建立连接就可以直接发送数据报的服务。UDP的主要目的是在IP网络上快速、高效地传输数据,而不保证数据的可靠传输。

工作原理
在UDP通信中,发送方应用程序将数据封装成UDP数据报,然后直接发送给接收方。UDP数据报包括一个UDP头部和应用层数据。UDP头部包含源端口号、目的端口号、数据报长度和校验和等信息。
源端口号是发送方应用程序的端口号,用于标识发送方的应用程序;目的端口号是接收方应用程序的端口号,用于标识接收方的应用程序。数据报长度表示整个UDP数据报的长度,包括头部和数据部分。校验和用于检查UDP数据报在传输过程中是否出现错误。
当接收方收到UDP数据报后,根据目的端口号将数据报交给对应的应用程序。UDP不进行数据的确认、排序、重传等操作,这些操作由应用层自己来处理。

特点
无连接:UDP在发送数据之前不需要建立连接。这使得UDP的开销较小,因为它不需要像TCP那样进行三次握手来建立连接,也不需要进行四次挥手来释放连接。例如,在一些实时性要求很高的多媒体应用(如网络视频会议)中,UDP可以快速地将视频数据发送出去,减少建立连接的延迟。
不可靠传输:UDP不保证数据的可靠传输。它不会对丢失的数据报进行重传,也不会对乱序的数据报进行排序。如果数据报在传输过程中丢失或者出现错误,UDP不会主动进行修复。这种不可靠性使得UDP在一些对可靠性要求不高的场景下很有优势,比如在一些简单的查询服务(如DNS查询)中,即使偶尔丢失一个查询请求,也不会对整个系统产生严重的影响。
速度快:由于UDP没有复杂的错误检查和恢复机制,数据传输速度相对较快。它适合于对实时性要求高、对数据完整性要求相对较低的应用场景。例如,在一些在线游戏场景中,玩家的操作指令通过UDP快速传输,即使偶尔丢失一些指令数据,也不会对游戏的整体体验产生太大的影响,因为游戏可以根据后续的操作指令进行一定的补偿。

应用场景
实时音频/视频通信:像Skype、Zoom等视频会议软件在传输音频和视频数据时会使用UDP。因为在这些应用中,实时性是至关重要的。即使数据有少量丢失,也不会对语音和视频的质量产生致命的影响。而且UDP能够快速地将数据发送出去,减少延迟,保证通信的流畅性。
DNS(域名系统):DNS查询通常使用UDP。当客户端向DNS服务器查询域名对应的IP地址时,UDP可以快速地将查询请求发送给DNS服务器,并且DNS查询对数据的可靠性要求相对较低,即使偶尔丢失一个查询请求,客户端可以重新发起查询。
在线游戏:在一些简单的在线游戏中,如一些休闲小游戏,游戏服务器和客户端之间的交互数据(如玩家的操作指令、游戏状态更新等)通过UDP传输。UDP能够快速地将这些数据发送出去,保证游戏的实时性

QT实现步奏
在Qt中实现UDP同步收发数据,主要依赖于QUdpSocket类。QUdpSocket提供了基于UDP协议的网络通信功能。同步收发数据意味着在发送数据后,程序会等待接收响应,直到数据被接收或者超时。这种模式在一些简单的查询 - 响应场景中很有用。以下是实现UDP同步收发数据的基本步骤和代码示例:

  1. 包含必要的头文件
    cpp
    #include
    #include
    #include
  2. 创建QUdpSocket对象
    cpp
    QUdpSocket *udpSocket = new QUdpSocket(this);
  3. 配置UDP套接字
    绑定本地端口:如果需要接收数据,需要绑定一个本地端口。
    设置超时机制:可以通过QTimer来实现超时机制。
  4. 发送数据
    使用QUdpSocket::writeDatagram()方法发送数据。
  5. 接收数据
    使用QUdpSocket::readDatagram()方法接收数据。
    在超时时间内等待数据接收完成。
  6. 处理超时
    如果在超时时间内没有收到数据,可以认为接收失败

QT案列
UDP 接收端(服务器):监听 12345 端口,打印收到的数据
UDP 发送端(客户端):向 127.0.0.1:12345 发送 “Hello UDP”
整个工程采用 Qt6 + CMake;如用 qmake,仅需把 CMakeLists.txt 换成 .pro 即可。

1,工程架构
QtUdpDemo/
├ CMakeLists.txt
├ src/
│ ├ main.cpp // 启动界面(两个按钮:发送 / 接收)
│ ├ UdpReceiver.h/.cpp // 接收端
│ └ UdpSender.h/.cpp // 发送端

2.CMakeLists.txt

cmake_minimum_required(VERSION 3.16)
project(QtUdpDemo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Network)
qt_standard_project_setup()
qt_add_executable(QtUdpDemo
    src/main.cpp
    src/UdpReceiver.cpp
    src/UdpSender.cpp
)
target_link_libraries(QtUdpDemo Qt6::Core Qt6::Widgets Qt6::Network)

3.UdpReceiver.h / UdpReceiver.cpp

// UdpReceiver.h
#pragma once
#include <QWidget>
#include <QUdpSocket>
#include <QTextEdit>

class UdpReceiver : public QWidget {
    Q_OBJECT
public:
    explicit UdpReceiver(QWidget *parent = nullptr);
private slots:
    void readPendingDatagrams();
private:
    QUdpSocket *socket;
    QTextEdit  *log;
};
// UdpReceiver.cpp
#include "UdpReceiver.h"
#include <QVBoxLayout>
#include <QHostAddress>

UdpReceiver::UdpReceiver(QWidget *parent)
    : QWidget(parent), socket(new QUdpSocket(this)), log(new QTextEdit(this)) {
    setWindowTitle("UDP Receiver - port 12345");
    QVBoxLayout *lay = new QVBoxLayout(this);
    lay->addWidget(log);

    bool ok = socket->bind(QHostAddress::Any, 12345);
    log->append(ok ? "Bind OK" : socket->errorString());
    connect(socket, &QUdpSocket::readyRead,
            this,   &UdpReceiver::readPendingDatagrams);
}

void UdpReceiver::readPendingDatagrams() {
    while (socket->hasPendingDatagrams()) {
        QNetworkDatagram dgram = socket->receiveDatagram();
        log->append(QString("From %1:%2  ->  %3")
                    .arg(dgram.senderAddress().toString())
                    .arg(dgram.senderPort())
                    .arg(QString(dgram.data())));
    }
}

4.dpSender.h / UdpSender.cpp

// UdpSender.h
#pragma once
#include <QWidget>
#include <QUdpSocket>
class QLineEdit;
class QPushButton;

class UdpSender : public QWidget {
    Q_OBJECT
public:
    explicit UdpSender(QWidget *parent = nullptr);
private slots:
    void sendDatagram();
private:
    QUdpSocket *socket;
    QLineEdit  *input;
    QPushButton *btn;
};
// UdpSender.cpp
#include "UdpSender.h"
#include <QVBoxLayout>
#include <QHostAddress>

UdpSender::UdpSender(QWidget *parent)
    : QWidget(parent), socket(new QUdpSocket(this)) {

    setWindowTitle("UDP Sender");
    input = new QLineEdit("Hello UDP");
    btn   = new QPushButton("Send");
    QVBoxLayout *lay = new QVBoxLayout(this);
    lay->addWidget(input);
    lay->addWidget(btn);

    connect(btn, &QPushButton::clicked, this, &UdpSender::sendDatagram);
}

void UdpSender::sendDatagram() {
    QByteArray data = input->text().toUtf8();
    socket->writeDatagram(data, QHostAddress("127.0.0.1"), 12345);
}

5.main.cpp(启动两个窗口)

#include <QApplication>
#include "UdpReceiver.h"
#include "UdpSender.h"

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    UdpReceiver recv;
    recv.show();
    UdpSender send;
    send.show();
    return a.exec();
}
Qt类 / 函数 作用
QUdpSocket::bind() 绑定本地端口,开始监听
writeDatagram() 发送数据报
readyRead() 信号 有数据到达时触发
receiveDatagram() 读取完整数据报 + 对端信息
QNetworkDatagram Qt6 推荐的新数据结构,一次拿到数据 + 元信息