尽管没有在任何地方使用std::thread
或QThread
,但仍然遇到以下问题:
- 始终是来自Qt的运行时调试错误日志:
QObject::connect:无法对类型为"QAbstractSocket::SocketError">
的参数进行排队(确保使用 qRegisterMetaType(( 注册"QAbstractSocket::SocketError"。 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个问题?
我认为问题与线程无关,而是导致问题的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
将返回 null 指针。
因此,如果连接排队,则必须注册信号使用的所有参数,无论它们是否槽使用。 为避免错误,请确保调用...
qRegisterMetaType<QAbstractSocket::SocketError>();
一次,在任何调用使用QAbstractSocket::SocketError
参数和 Qt::QueuedConnection 组合的connect
之前。
不,套接字不会为读/写创建单独的线程。相反,每当观察到读/写时,操作系统都会在给定的套接字描述符上引发事件。此事件应排队。因此,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
事件使程序崩溃。不幸的是,在使用std::exception
s C++时无法阻止它。
下面帖子中提到的解决方案没有帮助:如何防止SIGPIPE(或正确处理它们(
这可以通过以下方法避免:
if(pSocket->isValid())
pSocket->sendBinaryMessage(QByteArray(...));
因此,isValid()
套接字试图将某些内容写入已经断开连接的远程套接字连接的情况下会有所帮助。