When subclassing QTcpServer, how can I delay the emission of a newConnection () signal?

I want to create an SSL server, so I subclassed QTcpServer, and I override incomingConnection() , where I create QSslSocket , set its handle and call QSslSocket::startServerEncryption . At this point, I need to wait until the QSslSocket::encrypted() signal QSslSocket::encrypted() out, and only after that will my server emit the newConnection() signal. Client code will then think about it with QTcpSocket, but will actually use a secure socket.

But QTcpServer always emits newConnection() after calling incomingConnection() (I searched in the source for QTcpServer )

 void QTcpServerPrivate::readNotification() { // ......... q->incomingConnection(descriptor); QPointer<QTcpServer> that = q; emit q->newConnection(); // ......... } 

So my question is: is there a way to prevent QTcpServer from emitting newConnection() until I am ready to emit it myself?

The reason I want it is because I want my class to be used as a replacement for replacing QTcpServer with code that doesn't know that it uses it, so it should behave exactly like QTcpServer:

 QTcpServer* getServer(bool ssl) { return ssl ? new SslServer : new QTcpServer; } 

My code for the SslServer class currently:

 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; } } 
+1
source share
3 answers

Here is an idea that could work in this case: override the newConnection signal in your QTcpServer subclass.

If you do this, the objects associated with your server instance will not receive the QTcpServer "version" of this signal, only the one that you emit directly from your subclass.

Here's a proof of concept: class A is a QTcpServer , foo is the signal you are trying to β€œcapture”, bar is just another (hypothetical) QTcpServer signal that you put on No need to touch.

 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); }; 

Class B is your subclass. Note that it overrides the foo signal, but does nothing for 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); }; 

Class C is a potential client that connects signals / slots to instance B in the same way as for instance 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 function }; public slots: void foo(int i) { qDebug() << "foo: " << i; } void bar(int i) { qDebug() << "bar: " << i; } }; 

Here is the result of constructing a C :

 B::doit // this is /* 1 */ foo: 2 bar: 2 A::doit // this is /* 2 */ bar: 1 

... and nothing else. A emit foo(1) not connected to the C foo slot, it will never come to C A emit bar(1) worked as expected, this signal is not affected.

With this setup, you can issue newConnection when your class is ready, the QTcpServer version of the signal will not be received by your user objects.

+2
source

To be a true drop in replacement, you probably need to edit the actual Qt source, since you usually cannot override any calls to the Private class.

If you use a single replacement and you manage classes that connect to the newConnection signal ...

Just connect newConnection to your handleNewConnection slot. When the secure connection is ready to emit myNewConnection and connect it to the elements that would be connected to newConnection .

EDIT: After a little digging, I found the opportunity to reconnect the signal:

http://qt-project.org/forums/viewthread/6820

Basically, you override QObject::connect , and then track the connections and process them the way you want. Thus, in this case, you save the list of all connections of the newConnection signal and save it in the list, so when you turn it off, you can reconnect it. Be sure to call QObject::connect at the end of the reimplementation.

Another option when navigating this route would be to go over and simply redirect connections there. When a connection is requested from newConnection , move it there myNewConnection .

Hope this helps.

+1
source

A dirty hack would have to block the signals from QTcpServer very shortly. Since you know that newConnection() will be released immediately after returning from SslServer::incomingConnection() , call this->blockSignals(true); immediately before returning. This will prevent newConnection() from calling any slots to which it is connected.

To receive subsequent signals, unlock the signals as soon as you can. I assume that the earliest time would be correct when control returns to the event loop, so QTimer :: singleShot can do this.

 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); } 

The disadvantage of this is that you lose every signal that can be legitimately emitted between incomingConnection() and unblockSignals() . As I said, this is a dirty hack.

+1
source

All Articles