尽管未在任何地方使用std::thread
或QThread
,仍然会遇到以下问题:
QObject :: connect:无法对类型为'QAbstractSocket :: SocketError'的参数进行排队 (确保使用qRegisterMetaType()注册'QAbstractSocket :: SocketError'。)
TcpSocket::flush()
方法的间歇性崩溃; SIGPIPE
在搜索互联网时,发现人们建议修复第一个问题(即元错误),当我们有多个线程时,我需要使用qRegisterMetaType()
进行注册。
同样的多线程也被称为第二个问题的原因;请参阅this和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是否自己创建任何内部线程用于读/写目的? (我希望不是)。如何解决以上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()
在套接字试图向已经断开的远程套接字连接中写入内容的情况下很有用。