You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt中QUdpSocket仅接收前2个数据报的问题排查与解决

Qt UDP图片传输问题:大文件拆分后仅接收前2个数据报

问题现象

在Qt环境下实现UDP协议传输图片,当图片大于50KB时拆分多个数据报发送,客户端已发送全部数据报,但服务端仅收到前2个。只有图片小于50KB或拆分后为2个数据报时,传输正常。

问题原因

  1. 接收逻辑的致命缺陷:服务端slotProcessDatagrams槽函数中,每次readyRead信号触发时都会创建局部的datagram变量来临时存储数据。但UDP的readyRead信号可能分批触发——当后续数据报延迟到达时,新的信号会重新初始化空的datagram,导致之前接收的片段丢失,永远无法拼接出完整的图片。
  2. 缺少分片重组的必要机制:拆分的数据报没有序号、总数量等标识,服务端无法判断是否接收完整,也无法处理UDP可能出现的乱序、丢包问题。
  3. 数据报大小超出MTU限制:当前设置的UDP_DATAGRAM_SIZE=50000远大于以太网MTU(1500字节,扣除IP和UDP头后有效载荷约1472字节),会被IP层强制分片,大幅提升丢包概率。

解决思路

  • 修正服务端缓存逻辑:在UDP_Server类中添加全局成员变量作为接收缓冲区,替代槽函数内的局部变量,持续累积所有收到的分片,直到确认接收完整。
  • 添加自定义分片头部:每个数据报前附加头部信息,包含总分片数、当前分片序号、图片总大小等。客户端先发送头部信息,再发送分片;服务端根据序号拼接,直到收全所有分片后再解析图片。
  • 增加可靠性校验机制:服务端收到每个分片后向客户端发送ACK确认,客户端未收到ACK则重发对应分片,弥补UDP的不可靠性。
  • 调整分片大小:将单个数据报大小设置为不超过1472字节,避免IP层自动分片,降低丢包风险。

相关代码

main.cpp

#include <QApplication>

#include "udp_server.h"
#include "udp_client.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    UDP_Server server(2323);
    UDP_Client client(2323);

    server.show();
    client.show();

    return a.exec();
}

udp_server.h 和 udp_server.cpp

#ifndef UDP_SERVER_H
#define UDP_SERVER_H

#include <QWidget>
#include <QUdpSocket>
#include <QBoxLayout>
#include <QLabel>
#include <QBuffer>
#include <QFile>

class UDP_Server : public QWidget
{
    Q_OBJECT
public:
    explicit UDP_Server(int port, QWidget *parent = nullptr);

private:
    QUdpSocket* socket;
    QLabel* showLabel;


private slots:
    void slotProcessDatagrams();

};

#endif // UDP_SERVER_H

//================================

#include "udp_server.h"

UDP_Server::UDP_Server(int port, QWidget *parent)
    : QWidget{parent}
{
    setWindowTitle("SERVER");

    socket = new QUdpSocket(this);
    socket->bind(QHostAddress::LocalHost, port);
    connect(socket, SIGNAL(readyRead()), SLOT(slotProcessDatagrams()));

    showLabel = new QLabel;

    QVBoxLayout* box = new QVBoxLayout;
    box->addWidget(showLabel);
    setLayout(box);
}

void UDP_Server::slotProcessDatagrams() {
    QByteArray datagram;
    QPixmap frame;

    while (socket->hasPendingDatagrams()) {
        QByteArray receivedData;
        receivedData.resize(socket->pendingDatagramSize());
        socket->readDatagram(receivedData.data(), receivedData.size());
        datagram.append(receivedData);
        qDebug() << "read: " << receivedData.size();
    }

    if (frame.loadFromData(datagram, "PNG")) {
        showLabel->setPixmap(frame);
    } else {
        qDebug() << "Failed to read frame";
    }
}

udp_client.h 和 udp_client.cpp

#ifndef UDP_CLIENT_H
#define UDP_CLIENT_H

#include <QWidget>
#include <QUdpSocket>
#include <QPushButton>
#include <QBoxLayout>
#include <QTextEdit>
#include <QBuffer>
#include <QFile>

class UDP_Client : public QWidget
{
    Q_OBJECT
public:
    explicit UDP_Client(int port, QWidget *parent = nullptr);

private:
    QUdpSocket* socket;
    int port;

private slots:
    void slotSendDatagram();

};

#endif // UDP_CLIENT_H

//================

#include "udp_client.h"

UDP_Client::UDP_Client(int port, QWidget *parent)
    : QWidget{parent}, port(port)
{
    setWindowTitle("CLIENT");

    socket = new QUdpSocket(this);

    QPushButton* btn = new QPushButton("Send");
    connect(btn, SIGNAL(clicked()), SLOT(slotSendDatagram()));

    QVBoxLayout* box = new QVBoxLayout;
    box->addWidget(btn);
    setLayout(box);

}

const int UDP_DATAGRAM_SIZE = 50000;

void UDP_Client::slotSendDatagram() {
    QFile file("path/to/image");
    if (file.open(QIODevice::ReadOnly)) {
        while (!file.atEnd()) {
            QByteArray datagram = file.read(UDP_DATAGRAM_SIZE);
            socket->writeDatagram(datagram, QHostAddress::LocalHost, port);
            qDebug() << "send: " << datagram.size();
        }
        file.close();
    }

}

调试输出

send: 50000
send: 50000
send: 50000
send: 24753

read: 50000
read: 50000
Failed to read frame

内容的提问来源于stack exchange,提问作者TuleKr1s

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 20:45:08