QTcpSocket或QSslSocket是否自动创建用于读/写的线程?

时间:2017-07-13 10:14:32

标签: c++ multithreading qt qtcpsocket qsslsocket

尽管未在任何地方使用std::threadQThread,仍然会遇到以下问题:

  1. 始终来自Qt的运行时调试错误日志:
      

    QObject :: connect:无法对类型为'QAbstractSocket :: SocketError'的参数进行排队   (确保使用qRegisterMetaType()注册'QAbstractSocket :: SocketError'。)

  2. TcpSocket::flush()方法的间歇性崩溃;
    我使用这种方法来确保立即写入TCP;现在,有时应用程序会使用SIGPIPE
  3. 与此方法完全崩溃

    在搜索互联网时,发现人们建议修复第一个问题(即元错误),当我们有多个线程时,我需要使用qRegisterMetaType()进行注册。
    同样的多线程也被称为第二个问题的原因;请参阅thisthis

    但我没有超过1个帖子!
    我的套接字代码如下所示:

    struct Socket : public QSslSocket
    {
      Q_OBJECT public:
    
      void ConnectSlots ()
      {
        const auto connectionType = Qt::QueuedConnection;
        connect(this, SIGNAL(readyRead()), this, SLOT(ReceiveData()), connectionType);
        connect(this, SIGNAL(disconnected()), this, SLOT(Disconnected()), connectionType);
        connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
                this, SLOT(Error(QAbstractSocket::SocketError)), connectionType);
        //                           ^^^^^^^ error comes whether I comment this or not
      }
    
      public slots:
      void ReceiveData () { ... }
      void Disconnected () { ... }
      void Error () { ... }
    }
    

    问题:Qt是否自己创建任何内部线程用于读/写目的? (我希望不是)。如何解决以上2个问题?

2 个答案:

答案 0 :(得分:3)

我不认为问题与线程有关,而是导致问题的QAbstractSocket::SocketError类型参数 Qt::QueuedConnection的组合。

查看Qt5.8源中的各种connect实现,如果将Qt::QueuedConnection指定为连接类型,则将执行对信号参数类型的检查。有点像...

int *types = 0;
if ((type == Qt::QueuedConnection)
        && !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
   return QMetaObject::Connection(0);
}

如果未注册任何类型,queuedConnectionTypes将返回空指针。

因此,如果连接排队,那么信号使用的所有参数必须注册,无论它们是否被插槽使用。为避免错误,请务必致电...

qRegisterMetaType<QAbstractSocket::SocketError>();

在使用connect参数和Qt :: QueuedConnection的组合的任何QAbstractSocket::SocketError调用之前的某个时刻。

答案 1 :(得分:0)

否,套接字不会为读取/写入创建单独的线程。相反,只要观察到读/写,OS就会在给定的套接字描述符上引发事件。此事件应排队。因此,Qt::QueuedConnection是首选。

QAbstractSocket::SocketError即兴出现并且显示为特定于操作系统。这是无法避免的。最多可以在发生此类错误时销毁套接字。

为避免崩溃,每当套接字断开连接时,都可以执行以下操作:

void Destroy (QWebSocket* const pSocket)
{
  if(pSocket == nullptr)
    return;
  pSocket->disconnect();  // no further signal/slot
  pSocket->close();  // graceful closure
  pSocket->deleteLater(); // don't delete immediately; let the Qt take care
  pSocket = nullptr; // to avoid further undefined behaviour
}

即使在执行上述操作之后,有时也会由于write()操作而导致套接字崩溃。即当套接字close() -es时,它尝试flush()的所有可写数据。在此期间,如果远程连接已经关闭,则操作系统会使用SIGPIPE事件使程序崩溃。不幸的是,在C ++中使用std::exception是无法避免的。

以下帖子中提到的解决方案无济于事:
How to prevent SIGPIPEs (or handle them properly)
可以通过以下方法避免这种情况:

if(pSocket->isValid())
  pSocket->sendBinaryMessage(QByteArray(...));

因此,isValid()在套接字试图向已经断开的远程套接字连接中写入内容的情况下很有用。