QTcpSocket 或 QSslSocket 是否自动为 reading/writing 创建线程?
Do QTcpSocket or QSslSocket automatically create thread for reading/writing?
尽管没有在任何地方使用 std::thread
或 QThread
,仍然遇到以下问题:
- 总是来自 Qt 的运行时调试错误日志:
QObject::connect: Cannot queue arguments of type 'QAbstractSocket::SocketError'
(Make sure 'QAbstractSocket::SocketError' is registered using qRegisterMetaType().)
TcpSocket::flush()
方法间歇性崩溃;
我使用这种方法来确保立即写入 TCP;现在,有时应用程序会在使用 SIGPIPE
时恰好在此方法中崩溃
在互联网上搜索后,发现人们建议解决第一个问题(即元错误),当我们有多个线程时,我需要使用 qRegisterMetaType()
进行注册。
相同的多线程也被称为第二个问题的原因;参见 this and this。
但是我的线程不超过 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 是否为了 read/write 目的自行创建任何内部线程? (我希望不是)。如何解决以上 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
将 return 为空指针。
因此,如果连接排队,则必须注册 信号 使用的所有参数,无论它们是否被插槽使用。为避免错误,请确保您调用...
qRegisterMetaType<QAbstractSocket::SocketError>();
在任何使用 QAbstractSocket::SocketError
参数和 Qt::QueuedConnection.
的组合调用 connect
之前的某个时间点
不,套接字不会为 read/write 创建单独的线程 。相反,只要观察到 read/write,OS 就会在给定的套接字描述符上引发事件。这个事件应该排队。因此,Qt::QueuedConnection
是首选。
QAbstractSocket::SocketError
是即兴的,看起来 OS 是特定的。这是无法避免的。最多可以在发生此类错误时销毁套接字。
为避免崩溃,每当套接字断开连接时,可以执行以下操作:
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()
所有可写数据。在此期间,如果远程连接已经关闭,则 OS 使用 SIGPIPE
事件使程序崩溃。不幸的是,它无法在 C++ 中使用 std::exception
s.
来阻止。
下面提到的解决方案post没有帮助:
How to prevent SIGPIPEs (or handle them properly)
这可以通过以下方式避免:
if(pSocket->isValid())
pSocket->sendBinaryMessage(QByteArray(...));
因此 isValid()
在套接字试图将某些内容写入已断开的远程套接字连接的情况下有所帮助。
尽管没有在任何地方使用 std::thread
或 QThread
,仍然遇到以下问题:
- 总是来自 Qt 的运行时调试错误日志:
QObject::connect: Cannot queue arguments of type 'QAbstractSocket::SocketError'
(Make sure 'QAbstractSocket::SocketError' is registered using qRegisterMetaType().) TcpSocket::flush()
方法间歇性崩溃;
我使用这种方法来确保立即写入 TCP;现在,有时应用程序会在使用SIGPIPE
时恰好在此方法中崩溃
在互联网上搜索后,发现人们建议解决第一个问题(即元错误),当我们有多个线程时,我需要使用 qRegisterMetaType()
进行注册。
相同的多线程也被称为第二个问题的原因;参见 this and this。
但是我的线程不超过 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 是否为了 read/write 目的自行创建任何内部线程? (我希望不是)。如何解决以上 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
将 return 为空指针。
因此,如果连接排队,则必须注册 信号 使用的所有参数,无论它们是否被插槽使用。为避免错误,请确保您调用...
qRegisterMetaType<QAbstractSocket::SocketError>();
在任何使用 QAbstractSocket::SocketError
参数和 Qt::QueuedConnection.
connect
之前的某个时间点
不,套接字不会为 read/write 创建单独的线程 。相反,只要观察到 read/write,OS 就会在给定的套接字描述符上引发事件。这个事件应该排队。因此,Qt::QueuedConnection
是首选。
QAbstractSocket::SocketError
是即兴的,看起来 OS 是特定的。这是无法避免的。最多可以在发生此类错误时销毁套接字。
为避免崩溃,每当套接字断开连接时,可以执行以下操作:
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()
所有可写数据。在此期间,如果远程连接已经关闭,则 OS 使用 SIGPIPE
事件使程序崩溃。不幸的是,它无法在 C++ 中使用 std::exception
s.
下面提到的解决方案post没有帮助:
How to prevent SIGPIPEs (or handle them properly)
这可以通过以下方式避免:
if(pSocket->isValid())
pSocket->sendBinaryMessage(QByteArray(...));
因此 isValid()
在套接字试图将某些内容写入已断开的远程套接字连接的情况下有所帮助。