QT的UDP

定义

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

cpp 复制代码
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

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;
};
cpp 复制代码
// 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

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;
};
cpp 复制代码
// 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(启动两个窗口)

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 推荐的新数据结构,一次拿到数据 + 元信息
相关推荐
小灰灰搞电子40 分钟前
Qt 使用QtXlsx库处理Excel文件
qt·excel
刃神太酷啦1 小时前
C++ 容器适配器与核心数据结构精解:栈、队列、deque 底层实现与实战应用----《Hello C++ Wrold!》(17)--(C/C++)
java·c语言·数据结构·c++·qt·算法·leetcode
feiyangqingyun2 小时前
Qt结合ffmpeg实现图片参数调节/明亮度对比度饱和度设置/滤镜的使用
qt·ffmpeg·明亮度饱和度对比度
程序员编程指南9 小时前
Qt 嵌入式 Linux 系统定制全指南
linux·c语言·开发语言·c++·qt
渡我白衣13 小时前
Linux网络编程:基于UDP 的聊天室雏形
linux·网络·udp
技术不支持14 小时前
Qt Creator 11.0.3 语法高亮bug问题
java·服务器·数据库·qt·bug
渡我白衣14 小时前
Linux网络编程:UDP 的echo server
linux·网络·udp
zhangzhangkeji18 小时前
QT6 源,十章绘图(2)画刷 QBrush:刷子只涉及填充颜色,线型,填充图片,以及变换矩阵这几个属性,附源代码带注释。
qt
lzb_kkk1 天前
【实习总结】Qt通过Qt Linguist(语言家)实现多语言支持
开发语言·c++·qt·1024程序员节·qt linguist·qt 语言家