【问题标题】:Qt send file over TCPQt 通过 TCP 发送文件
【发布时间】:2026-02-10 07:20:09
【问题描述】:

我在单个应用程序中执行 TCP 文件客户端-服务器。

首先,我每 50000 字节发送文件名和文件的和平。

客户:

void client::sendFile(QString path)
{
    QDataStream out(cl);
    QFile toSend(path);

    if (toSend.open(QIODevice::ReadOnly))
    {
        int s = 0;

        QFileInfo fileInfo(toSend.fileName());
        QString fileName(fileInfo.fileName());
        out << fileName;

        while (!toSend.atEnd())
        {
            QByteArray rawFile;
            rawFile = toSend.read(50000);
            out << rawFile;
            qDebug() << "ToSend"<<rawFile.size();
            s+=rawFile.size();
        }

        qDebug() << "Total:" << s;
    }
}

...我猜这是正确的

服务器(ReadyRead 插槽):

void server::receive()
{
    QTcpSocket *sender = (QTcpSocket*) this->sender();

    QDataStream in(sender);

    QString fName;
    in >> fName;
    QFile newFile("D:\\"+fName);

    if (newFile.open(QIODevice::WriteOnly))
    {
        while(sender->bytesAvailable())
        {
             QByteArray z;
             in >> z;
             newFile.write(z);
             qDebug () << "Received " << z.size();
        }

        newFile.close();
    }
}

...这里的问题是:while 在第一次迭代后损坏。

看:

ToSend 50000 
ToSend 50000 
ToSend 50000 
ToSend 31135 
Total: 181135 
Received 50000 

如您所见,只收到 1 个块而不是 4 个。

如何解决这个问题?为什么 bytesAvailable 在不应该返回 0 时返回? 很高兴知道这种通过 tcp 接收文件的好方法是否也可以:)

【问题讨论】:

  • 我认为在您的接收器中,您必须等到收到 181135 个字节。

标签: qt file tcp


【解决方案1】:

可能是因为只有这一个数据包可用。您需要在发送数据之前设置有关文件大小的协商。然后等待所有数据传输完毕。处理完前 50k 数据后,BytesAvailable() 立即返回 false。如果你再次调用 bytesAvailable() 等待 1 秒,你可能会得到另一个数据包。

在发送数据之前实现一个 4 字节的“大小数据包”并检查是否收到了这些字节。

所以你的协议将是:

  • 包大小
  • 数据包
  • n次数据包

另一种选择是使用预定义的结束数据包,但如果您的文件可以在原始数据中包含此数据包,这可能会很危险。如果您可以确保您的数据不包含您的最终数据包,您最终将实现该协议:

  • 数据包
  • n次数据包
  • 结束数据包

【讨论】:

    【解决方案2】:

    您必须“发明”一种协议,让接收方知道将传输多少数据。 bytesAvailable 仅向您显示到目前为止接收到的字节数以及可以从流中读取的字节数,但它不知道发送方将来仍将传输多少数据。

    请参阅here,了解如何执行此操作的非常简单的示例。

    【讨论】:

      【解决方案3】:

      问题是你的循环等待 bytesAvailable()。

      readyRead 插槽旨在处理此问题。当您的插槽被调用时,读取可用的字节数并处理它们。然后你应该从函数中返回。

      当有更多数据可用时,您将在 readyRead 槽函数中再次调用。

      当然,这意味着您需要在连续调用之间缓冲数据,建议通过发送服务器期望整个文件的字节数来启动该过程。一旦服务器积累了该字节数,它就可以写入新文件。

      【讨论】: