如何使用Qt UDP套接字发送和接收大数据?

3
我想使用Qt UDP(而不是TCP)套接字来传输文件。 所以我编写了以下代码:
发送器
QFile file1(QString::fromStdString(filedir));
QByteArray bytes;
file1.open(QIODevice::ReadOnly);
QTextStream in(&file1);
while (!in.atEnd()) {
    bytes = in.read(8192).toAscii();
    udpSocket.writeDatagram(bytes, QHostAddress(ip), port.toInt());
}

接收者
udpSocket1.bind(ui->sendPort->text().toInt());
connect(&udpSocket1,SIGNAL(readyRead()),this,SLOT(listenfile()));

void Widget::listenfile() {
    QFile file("received.txt");
    file.resize(0);
    file.open(QIODevice::Append);
    QTextStream out(&file);
    do {
        QByteArray data;
        data.resize(udpSocket1.pendingDatagramSize());
        udpSocket1.readDatagram(data.data(),data.size());
        QString str=data.data();
        ui->textBrowser1->append(str);
        out << data;
    } while (udpSocket1.hasPendingDatagrams());
}

当我发送小文件时,没有问题。但是,如果我想发送大文件(> 8192字节),接收方只能获取顶部8KB的数据,即使在本地测试也是如此。如果我减小发送者中的大小数字,例如1024,接收者仍然只获取顶部8KB的数据。如果我将发送者中的大小数字增加到X字节(X>8192),接收者将获取前X个字节的数据。
看起来Qt UDP数据包传输的最小大小为8192字节。接收方总是能够接收第一个数据包,但无法接收其他数据包。
我对Qt和网络编程的经验很少,所以我不知道我的猜测是否正确。您能告诉我如何更改这些代码以支持接收第一个数据包后的数据包,以便我可以传输大量数据吗?
2个回答

4

你的问题可能来自这一行代码:

} while (udpSocket1.hasPendingDatagrams());

你希望一次性接收整个数据包集,但更有可能的是部分数据包先到达,后续还有更多。因此,你需要监听套接字更长时间,并确定对端实际上已经发送完数据。


谢谢您的回复。您是说我应该使用另一个循环线程来处理数据报,还是有其他简单的方法?我认为将readyRead()信号连接到listenfile()插槽可以永久监听套接字。因为当发送方发送第二个文件时,接收方也可以获取顶部数据报而不是忽略它。(也许我错了) - WH's HeV
正确的做法是,当readyRead()信号被发出时,需要调用读取代码。问题在于如何检测一个文件何时结束,另一个文件何时开始。如果需要的话,你可以通过在数据包前面添加魔术字节来解决这个问题。例如,当有更多数据到来时,在数据包前始终放置一个值为0x00的1字节数据包,并在数据包前放置一个值为0x01的数据包,以表示它是最后一个数据包,这样你就知道这是文件的结尾,下一个数据包将是第二个文件的开头。但记得在保存之前去掉那个字节 :-) - Wes Hardaker
然而,这似乎并不是首选使用UDP的工作。我不确定为什么在发送大文件时要强制使用UDP。这更适合TCP。通常,您应该将数据块分成1400字节的数据包,以便UDP不需要分段。然后,还有一个问题,就是使用UDP可能会丢失数据包,而您无法得知。或者您可能会收到两个相同的数据包!想象一下这对您传输的文件会产生什么影响!再次强调,您应该真正使用TCP。 - Wes Hardaker

4
经过长时间的调试和使用Wireshark抓包,我认为问题的原因是QUdpSocket本身。就像互联网上的许多其他示例一样,我的代码可能是正确的。但是QUdpSocket类不适合传输大量数据,因为当与readyRead()连接的槽函数正在执行时,随后的数据包无法再次触发它,直到该函数完成。因此,发送方必须在发送一些数据后睡眠一段时间以等待接收方的槽函数。

结论是QUdpSocket类不是传输大量数据的可靠方法。我应该使用低级套接字API,自定义一些协议并设计多进程/多线程架构来从根本上解决问题。当然,使用TCP套接字也是另一个选择。


我已经遇到了同样的问题有一段时间了,它真的让我很头疼。在尝试了许多不同的QUdpSocket方法并度过了相当长时间后,最终我得出了同样的结论:QUdpSocket不适合处理高速数据流。低级别的winsock udp线程往往能更好地解决这个问题。但这真的,真的很遗憾。我希望Qt能够提出一个解决这个严重问题的方案。 - Doğukan Tunç

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接