使用 QIODevice 的阻塞接口(QTcpSocket 和 QFile)时何时检查错误

2024-01-05

出于学习目的,我制作了通过网络发送文件的应用程序(这对我来说非常有效)。这里我将发布主要部分的代码,实际发送字节的代码,我认为这就足够了。

我的主要问题是:我应该何时、何地、为何以及如何检查错误? (看起来不止一个问题:))

正如您所看到的,我通过检查每个可以以这种方式警告我的函数的返回值来检查错误(我用数字标记了每个检查,以便那些想要帮助回答和解释的人更容易)。

有这个必要吗?因为它可以显着扩展代码。

次要问题: 这是我做的好吗,​​有更好的方法吗?

while(!file->atEnd()){
   if(isCancelled())//this is in a thread, and there is mechanism to cancel it
       return;

   if((readed = file->read(inter_buffer,BUFLEN)) == -1){ //1 <- marking check with "1"
       emit errorOccurred(tr("Error while reading file."));
       return;
   }

   if(socket->write(inter_buffer,readed) == -1){//2 QTcpSocket::write
       emit errorOccurred(tr("Unable to send data. Probably the other side cancelled or there are connection problems."));
       qDebug() << socket->error();
       return;
   }

   rsofar += readed;

   if(!socket->flush()){//3
       emit errorOccurred(tr("Unable to send data. Probably the other side cancelled or there are connection problems."));
       return;
   }

   //emit signal to inform GUI thread about progress
   emit updateProgress((int)(((double)rsofar)/(double)filesize * 100.0));

   if(!socket->waitForBytesWritten()){//4
       //maybe this is not the right message, but that is not important now
      emit errorOccurred(tr("Unable to send data. Probably the other side cancelled or there are connection problems."));
       return;
   }

}

第三个问题是:在Java中我会依靠异常来处理这类问题。为什么Qt函数不抛出异常?是因为它对于 C++ 来说很慢(因为堆栈展开),还是只是用 C++ 编程时的坏习惯,或者因为它不能很好地处理信号和槽,或者其他什么?


异常可能会增加某些 C++ 实现的内存和运行时开销。这对于现代的、维护良好的 C++ 实现来说不是问题 - 但 Qt 必须在一些非常过时或尴尬的平台上运行和编译。不仅如此 - Qt(至少是核心)必须在禁用编译器异常支持的情况下正确编译和运行。

您的错误代码检查几乎是正确的。在你的情况下,如果write返回除以下以外的任何大小readed,应将其视为错误。语法挑剔:正确的形式是“read”,而不是“readed”。是的,你已经“写”了,但只是“读”了。英语就是这么奇怪;)

没有必要使用flush(). Just waitForBytesWritten然后检查还有多少字节需要写入,并据此报告进度。由于您的方法无法分摊磁盘文件访问的延迟,因此您的运行速度会变慢:您不会并行进行网络发送和文件读取。

所以,你所做的事情有点复杂。您不需要使用阻止waitForX完全没有功能。您正在线程中运行,因此我们只使用由QIODevice并使用默认的事件循环QThread's run()方法是旋转。这样您就可以在同一个工作线程中处理多个文件。您的实现需要一个专用线程来并行处理每个文件。

下面的代码应该可以工作。只需使用moveToThread将其转移给工人QThread- 不源自QThread。要开始发送,请调用start()投币口。要取消发送,您只需致电sender->deleteLater().

#include <QTcpSocket>
#include <QByteArray>

class Sender : public QObject {
    Q_OBJECT
    QIODevice * m_src;
    QAbstractSocket * m_dst;
    QByteArray m_buf;
    qint64 m_hasRead;
    qint64 m_hasWritten;
    qint64 m_srcSize;
    bool m_doneSignaled;
    bool signalDone()  {
        if (!m_doneSignaled &&
                ((m_srcSize && m_hasWritten == m_srcSize) || m_src->atEnd())) {
            emit done();
            m_doneSignaled = true;
        }
        return m_doneSignaled;
    }
    Q_SLOT void dstBytesWritten(qint64 len) {
        if (m_dst->bytesToWrite() < m_buf.size() / 2) {
            // the transmit buffer is running low, refill
            send();
        }
        m_hasWritten += len;
        emit progressed((m_hasWritten * 100) / m_srcSize);
        signalDone();
    }
    Q_SLOT void dstError() {
        emit errorOccurred(tr("Unable to send data. Probably the other side"
                              "cancelled or there are connection problems."));
        qDebug() << m_dst->error();
    }
    void send() {
        if (signalDone()) return;
        qint64 read = m_src->read(m_buf.data(), m_buf.size());
        if (read == -1) {
            emit errorOccurred(tr("Error while reading file."));
            return;
        }
        m_hasRead += read;
        qint64 written = m_dst->write(m_buf.constData(), read);
        if (written == -1) {
            emit errorOccurred(tr("Unable to send data. Probably the other side "
                                  "cancelled or there are connection problems."));
            qDebug() << m_dst->error();
            return;
        }
        if (written != read) {
            emit errorOccurred(tr("Internal error while filling write buffer."));
            qDebug() << m_dst->error();
            return;
        }
    }
public:
    /*! Requires a source device open for reading, and a destination socket open
        for writing. */
    Sender(QIODevice * src, QAbstractSocket * dst, QObject * parent = 0) :
        QObject(parent), m_src(src), m_dst(dst), m_buf(8192, Qt::Uninitialized),
        m_hasRead(0), m_hasWritten(0), m_doneSignaled(false)
    {
        Q_ASSERT(m_src->isReadable());
        Q_ASSERT(m_dst->isWritable());
        connect(m_dst, SIGNAL(bytesWritten(qint64)), SLOT(dstBytesWritten(qint64)));
        connect(m_dst, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(dstError()));
        m_srcSize = m_src->size();
    }
    Q_SLOT void start() { send(); }
    Q_SIGNAL void done();
    Q_SIGNAL void errorOccurred(const QString &);
    Q_SIGNAL void progressed(int percent);
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 QIODevice 的阻塞接口(QTcpSocket 和 QFile)时何时检查错误 的相关文章

随机推荐

  • Puppet 6 和模块 puppetlabs/accounts 不会以 Hiera YAML 格式创建用户帐户

    当我跑步时puppet agent test我没有错误输出 但用户没有创建 我的木偶 hira yaml 配置是 version 5 datadir etc puppetlabs code environments data hash ya
  • 使用 BFS 查找网格上对象的可能路径数

    我有一个代表网格的矩阵 并且想找出对象可以移动到的所有可能的位置 物体只能水平或垂直移动 假设下面的示例是我正在查看的网格 它表示为二维矩阵 对象是 0 是对象可以移动到的空白空间 1 是对象无法跳过或继续的墙壁 如果该对象只能水平或垂直移
  • Javascript 对象数组和 indexOf 的奇怪之处

    不太明白这里发生了什么 给定数组 arr first name Dan last name Woodson id 1 first name Jen last name Woodson id 2 first name Yoshi last n
  • 在特定变量上禁用 GCC“可能未初始化就使用”

    我在堆栈变量上收到此警告 warning object member may be used uninitialized in this function 在这种情况下 我不希望强制初始化只是为了消除警告 因为它会消耗 CPU 周期 该变量
  • 根据值降序对 Map 进行排序 [重复]

    这个问题在这里已经有答案了 可能的重复 如何在 Java 中对 Map 的值进行排序 https stackoverflow com questions 109383 how to sort a mapkey value on the va
  • 如何取消注册设置为 jQuery.ajaxStart() 函数的处理程序?

    我尝试做 ajaxStart function 但它不是取代它 而是附加的 如果我理解的话 你想删除ajaxStart来自元素的处理程序 如果是这样 只需使用 jQueryunbind 因为处理程序是作为事件附加的 selector unb
  • 将 Xpath 转换为 CSS 选择器

    我正在努力将以下 Xpath 转换为 CSS form id giftcard form div 3 div button 我知道我可以将 Xpath 用于我的 selenium JS 但由于某些奇怪的原因 它在我的情况下不起作用 我成功地
  • 私有继承、友元和异常处理

    当类 A 私有地继承自类 B 时 意味着 B 是 A 的私有基类子对象 但对于友元来说不是这样 对于友元来说 它是公共子对象 当有多个 catch 处理程序时 第一个匹配的处理程序 即 如果异常类型可以隐式转换为处理程序的参数类型 将被调用
  • Excel 在 CSV 导出中导出数百个空列

    在开始之前 我使用的是 Excel for Mac 2011 我在使用特定的 Excel 电子表格时遇到问题 当我将任何活动工作表导出为 CSV Windows CSV 格式 时 它会导出约 200 个空白列以及 7 或 8 个填充列 我过
  • 使用 jquery 从复选框列表附加文本

    我正在从事 ASP NET 项目 我的任务是附加Checkbox文本到TextBox 复选框的文本从数据库值绑定复选框列表 protected void prbtn Click object sender EventArgs e strin
  • C# 中的 WinForms Aero 向导?

    我正在寻找在 WinForms 中实现一个向导 C notWPF 我希望它符合 Aero Wizard 指南 谁能指点我一些示例代码吗 Codeplex 上有两个项目 航空玻璃供应商 http aeroglassprovider codep
  • 有没有类似Solr分析工具的Elasticsearch插件?

    Solr 内置了 分析画面 https cwiki apache org confluence display solr Analysis Screen 这有助于调试特定字段类型的分词器和过滤器之间的相互作用 ElasticSearch 有
  • 如何使用 AngularJS 指令中的编译函数来重复服务中的元素

    请有人告诉我需要做什么才能在指令中呈现存储在服务中的数组中的新元素 在下面的示例中 来自服务的警报显示每个新元素都已添加到 elements 数组中 但是如何使指令在页面上显示这些新元素呢 我尝试阅读有关的所有内容compile指令中的函数
  • 如何找出 .NET Windows 窗体中哪个控件具有焦点?

    如何找出哪个控件具有焦点Windows 窗体 http en wikipedia org wiki Windows Forms Form ActiveControl http msdn microsoft com en us library
  • 如何通过 super 关键字调用基类方法

    class base public void superMethod System out println Hello i m a super class method class der extends base super superM
  • DELETE FROM `table` AS `alias` ... WHERE `alias`.`column` ...为什么语法错误?

    我用 MySQL 尝试过 DELETE FROM contact hostcommands relation AS ContactHostCommand WHERE ContactHostCommand chr id 999999 LIMI
  • 配方还产生需要包装的本地输出

    我有一个配方 可以成功调用遗留构建命令来交叉编译目标 作为副作用 它会生成一些在构建中使用的自定义本机工具 我想将这些工具转化为 tools native包允许其他食谱依赖主包来访问工件 并使用 tools native包以进一步处理这些工
  • 如何在 Fabric.js 中的画布之间拖放

    据我所知 Fabric js 内置了对同一画布内拖放操作的支持 我们怎样才能让它适用于多个画布 或者来自非画布 html 元素 例如表格中的图像 在 Fabric js 中可以在画布之间进行拖放 但涉及对私有属性的一些操作 为此原因不保证它
  • 错误代码 128 是什么?

    什么是 git remote http 错误代码 128 当我尝试推送到需要身份验证的存储库时 它从不要求输入密码 就会发生这种情况 是否有一些中心位置可以找出错误代码的含义 128是默认的致命错误代码 这并不意味着它一定与身份验证有关 这
  • 使用 QIODevice 的阻塞接口(QTcpSocket 和 QFile)时何时检查错误

    出于学习目的 我制作了通过网络发送文件的应用程序 这对我来说非常有效 这里我将发布主要部分的代码 实际发送字节的代码 我认为这就足够了 我的主要问题是 我应该何时 何地 为何以及如何检查错误 看起来不止一个问题 正如您所看到的 我通过检查每