2

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

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

    QObject::connect: 不能对“QAbstractSocket::SocketError”类型的参数进行排队
    (确保使用 qRegisterMetaType() 注册了“QAbstractSocket::SocketError”。)

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

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

但我没有超过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个问题?

4

2 回答 2

3

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

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

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

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

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

qRegisterMetaType<QAbstractSocket::SocketError>();

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

于 2017-07-13T10:56:07.650 回答
0

不,套接字不会为读/写创建单独的线程。相反,只要观察到读/写,操作系统就会在给定的套接字描述符上引发事件。这个事件应该排队。因此,这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::exceptions 来防止。

以下帖子中提到的解决方案无济于事:
如何防止 SIGPIPE(或正确处理它们)
这可以通过以下方式避免:

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

isValid()在套接字试图将某些内容写入已经断开的远程套接字连接的情况下很有帮助。

于 2019-04-19T06:50:11.600 回答