[英]When subclassing QTcpServer, how can I delay emitting the newConnection() signal?
我想创建一个SSL服务器,所以我将QTcpServer子类化,并覆盖incomingConnection()
,在其中创建QSslSocket
,设置其描述符,并调用QSslSocket::startServerEncryption
。 此时我需要等待QSslSocket::encrypted()
信号,并且只有在此之后我的服务器newConnection()
发出newConnection()
信号。 客户端代码会认为它使用的是QTcpSocket,但事实上它将使用安全套接字。
但是QTcpServer在调用incomingConnection()
newConnection()
之后总是发出 newConnection()
(我查看了QTcpServer的源代码 ):
void QTcpServerPrivate::readNotification()
{
// .........
q->incomingConnection(descriptor);
QPointer<QTcpServer> that = q;
emit q->newConnection();
// .........
}
所以我的问题是, 有没有办法阻止QTcpServer
发出newConnection()
,直到我准备好自己发出它?
我想要这个的原因是我希望我的类能够被用作QTcpServer的替代品,通过不知道它正在使用它的代码,所以它必须像QTcpServer一样运行:
QTcpServer* getServer(bool ssl)
{
return ssl ? new SslServer : new QTcpServer;
}
我的SslServer类代码目前是这样的:
void SslServer::ready()
{
QSslSocket *socket = (QSslSocket *) sender();
addPendingConnection(socket);
emit newConnection();
}
void SslServer::incomingConnection(int socketDescriptor)
{
QSslSocket *serverSocket = new QSslSocket;
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(ready()));
serverSocket->startServerEncryption();
} else {
delete serverSocket;
}
}
这是一个可以在这种情况下工作的想法:重新定义QTcpServer
子类中的newConnection
信号。
如果这样做,与服务器实例连接的对象将不会收到QTcpServer
的信号“版本”,只接收您直接从子类发出的信号。
这是一个概念验证: A
类是QTcpServer
, foo
是你试图“劫持”的信号, bar
只是你不需要触摸的另一个(假设的) QTcpServer
信号。
class A: public QObject
{
Q_OBJECT
public:
A() {};
virtual void doit() {
qDebug() << "A::doit";
emit foo(1);
emit bar(1);
}
signals:
void foo(int);
void bar(int);
};
B
类是您的子类。 请注意,它重新定义了信号foo
,但没有做任何事情来bar
。
class B: public A
{
Q_OBJECT
public:
B() {};
virtual void doit() {
qDebug() << "B::doit";
emit foo(2);
emit bar(2);
}
signals:
void foo(int);
};
C
类是一个潜在的客户端,它从B
实例连接信号/槽,就像它对A
实例一样。
class C: public QObject
{
Q_OBJECT
public:
C() {
B *b = new B;
connect(b, SIGNAL(foo(int)), this, SLOT(foo(int)));
connect(b, SIGNAL(bar(int)), this, SLOT(bar(int)));
/* 1 */
b->doit();
/* 2 */
b->A::doit(); // call parent class's function
};
public slots:
void foo(int i) {
qDebug() << "foo: " << i;
}
void bar(int i) {
qDebug() << "bar: " << i;
}
};
这是构造C
的输出:
B::doit // this is /* 1 */
foo: 2
bar: 2
A::doit // this is /* 2 */
bar: 1
......别无其他。 A
的emit foo(1)
不连接到C
的foo
槽,它永远不会到达C
。 A
emit bar(1)
按预期工作,该信号未受影响。
使用该设置,您可以在类准备就绪时发出newConnection
,用户的对象将不会接收QTcpServer
的信号版本。
要成为真正的替代品,您可能需要编辑Qt的实际来源,因为您通常无法重新实现任何Private
类调用。
如果您是唯一一个使用替换的人,并且您控制连接到newConnection
信号的类...
只需将newConnection
连接到您自己的插槽handleNewConnection
。 当安全连接准备就绪时,发出myNewConnection
并将其连接到已连接到newConnection
的元素。
编辑:经过一番挖掘后,我找到了重新连接信号的选项:
http://qt-project.org/forums/viewthread/6820
基本上,您重新实现QObject::connect
,然后跟踪连接并按照您需要的方式处理它们。 因此,在这种情况下,您将保留信号newConnection
的所有连接的列表并将其保存在列表中,因此当您断开连接时,您可以重新连接它。 务必在重新实现结束时调用QObject::connect
。
走这条路线的另一个选择就是去重新路由那里的连接。 当从newConnection
请求连接时,将其移动到myNewConnection
。
希望有所帮助。
肮脏的黑客将非常短暂地阻止来自QTcpServer的信号。 既然你知道newConnection()
将发射你返回后立即SslServer::incomingConnection()
调用this->blockSignals(true);
就在你回来之前。 这将阻止newConnection()
调用它所连接的任何插槽。
为确保您收到后续信号,请尽快取消阻止信号。 我认为当控制回到事件循环时,可用的最早时间是正确的,所以QTimer :: singleShot可以做到这一点。
void SslServer::incomingConnection(int socketDescriptor)
{
QSslSocket *serverSocket = new QSslSocket;
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(ready()));
serverSocket->startServerEncryption();
} else {
delete serverSocket;
}
this -> blockSignals(true);
QTimer::singleShot(0, this, SLOT(unblockSignals());
}
void SslServer::unblockSignals()
{
this->blockSignals(false);
}
这样做的缺点是你将失去在incomingConnection()
和unblockSignals()
之间合法发出的每一个信号。 就像我说的那样,这是一个肮脏的黑客。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.