3

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

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

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

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

第二个问题:这是我做的好吗,​​有更好的方法吗?

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++ 中编程时的坏习惯,还是因为它不能很好地处理信号和插槽,或者其他什么?

4

1 回答 1

3

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

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

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

所以,你正在做的事情有点令人费解。您根本不需要使用阻塞waitForX函数。您正在线程中运行,所以让我们使用由提供的信号QIODevice并使用方法正在旋转QThread的默认事件循环。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);
};
于 2013-12-31T23:00:14.823 回答