【问题标题】:How to prevent file transfer from being interrupted (over TCP)如何防止文件传输被中断(通过 TCP)
【发布时间】:2017-09-05 22:56:48
【问题描述】:

我正在编写一个程序,以使用 Qt_5.9 在 C++ 中通过本地网络在客户端和服务器之间发送文件,并且传输随机停止(有时约为 400MB,但有时会更少)。

下面是在socket上写入文件内容的方法(在servertcp.cpp中):

void ServerTcp::write_file(QString fileName){
    QFile file(fileName);
    if(file.open(QIODevice::ReadOnly)){
        qint64 nb = 0;
        while(nb < file.size()){
            QByteArray partial = file.read(65536);
            clientConnection->write(partial, partial.count());
            // I think I have to write something here
            qint64 nbb = partial.count();
            nb += nbb;
            qDebug() << nbb << " (" << nb << ")";
        }

        file.close();

        clientConnection->write("[[[END]]]");
    }
}

这是读取(在发出readyRead 时调用)套接字(在clienttcp.cpp 中)的方法:

void ClientTcp::read()
{
    qDebug() << "To read : " << soc.size();

    emit to_IHM_text(soc.readAll());
}

我面临的问题是,在某些时候,客户端停止读取套接字(这意味着我们不再输入 read 方法,所以我猜 readyRead 没有发出),但服务器仍然写入其中(在任务管理器中,我可以看到内存使用量增加)。

谁能告诉我我做错了什么? (除了存在 FTP 时使用 TCP 进行文件传输) 我尝试在write_file 方法中插入clientConnection-&gt;waitForReadyRead(X),但无论套接字状态如何,它总是等待X ms。


编辑:

所以我更新了我的write_file 方法来检查连接状态:

void ServerTcp::write_file(QString fileName){
    QFile file(fileName);
    if(file.open(QIODevice::ReadOnly)){
        qint64 nb = 0;
        while(nb < file.size()){
            QByteArray partial = file.read(65536);

            if(clientConnection->bytesToWrite()>0){
                problem = true;
                qDebug() << clientConnection->bytesToWrite();
                clientConnection->waitForBytesWritten();
            }

            qDebug() << clientConnection->state();
            qint64 nbb = clientConnection->write(partial, partial.count());
            nb += nbb;
            qDebug() << nbb << " (" << nb << ")";
        }

        file.close();

        clientConnection->write("[[[END]]]");
    }
}

而且在所有文件传输过程中总是提示QAbstractSocket::ConnectedState,说明问题出在其他地方。

显然,即使clientConnection-&gt;write(...) 总是返回 65536(文件的最后一个块除外),套接字也会随机拒绝有效地写入字节(因为clientConnection-&gt;bytesToWrite() 在一段时间后返回 65536 的倍数)。

关于客户端知道它接收到整个文件的方式,我使用自制的握手,其中包含分开发送的标头(文件名 + 大小)和响应(可以接收或不可以接收)。


编辑 2:

我尝试了另一种方法(将套接字bytesWritten 信号连接到只发送文件的一小部分的方法),同样适用:在某些时候,似乎发生了拥塞......

【问题讨论】:

  • 据我所知,您没有检查连接是否处于活动状态。
  • @macroland 显然至少是在传输开始的时候,但是我怎样才能确保它保持活跃呢?
  • 您需要在服务器/客户端之间为自己创建一个keepAlive,例如每 500 毫秒。
  • 你能解释一下这部分吗:`我可以看到内存使用增加`应该是套接字缓冲区,而不是内存(RAM)
  • @Rafalon 请阅读:doc.qt.io/qt-5/qabstractsocket.html 关注功能:setReadBufferSize(qint64 size)qint64 readBufferSize() const

标签: c++ qt tcp


【解决方案1】:

根据您修改后的代码:

   while(nb < file.size()){
        QByteArray partial = file.read(65536);

        [...]

        qDebug() << clientConnection->state();
        qint64 nbb = clientConnection->write(partial, partial.count());
        nb += nbb;
    }

当上面的 write() 返回的值小于 partial.count() 时,你有想过会发生什么吗?

如果没有,我给你举个例子。说 write() 返回 32000 而不是 65536。这意味着 65536 字节 QByteArray 的前 32000 字节输出到 TCP 套接字;剩下的 33536 个字节会发生什么?没什么,就是这样——他们只是被忽略了,根本不去见客户。因此,客户端接收到的数据将只是原始文件包含的数据的一部分,因为数据流缺少来自文件中间各个位置的任意块。这可能不是您想要的行为。

处理 short-write() 问题的解决方法是仅使用剩余的 32000 字节数据再次调用 write()(并根据需要重复,直到部分缓冲区中的所有字节都被实际传递到套接字缓冲区,如 write() 调用的结果所示)。然后,也只有这样,是时候从文件中读取更多数据以发送了。

【讨论】:

  • 有趣,但在调试中它总是返回 65536,除了最后一个块,所以这个 bug 不是来自这里,即使它可能有
  • 另外,我尝试传输视频文件,我很确定收到的块顺序正确(因为我可以播放电影直到 4000 万左右)
猜你喜欢
  • 2011-02-27
  • 1970-01-01
  • 2015-11-19
  • 2014-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多