29664

When subclassing QTcpServer, how can I delay emitting the newConnection() signal?

Question:

I want to create an SSL server, so I subclass QTcpServer and I override incomingConnection(), where I create a QSslSocket, set its descriptor, and call QSslSocket::startServerEncryption. At this point I need to wait for QSslSocket::encrypted() signal to be emitted, and only after that should my server emit the newConnection() signal. The client code would then think it's using a QTcpSocket, but will in fact be using a secure socket.

But <strong>QTcpServer always emits</strong> newConnection() after calling incomingConnection() (I looked in <a href="http://qt.gitorious.org/qt/qt/blobs/4.8/src/network/socket/qtcpserver.cpp" rel="nofollow">the source of QTcpServer</a>):

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

So my question is, <strong>is there a way I can prevent QTcpServer from emitting newConnection(), until I'm ready to emit it myself?</strong>

The reason I want this is that I want my class to be able to be used as a drop-in replacement of QTcpServer, by code that is unaware it's using it, so it must behave exactly as a QTcpServer:

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

My code for the SslServer class is currently this:

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

Answer1:

Here's an idea that could work in this case: redefine the newConnection signal in your QTcpServer subclass.

If you do that, objects that connected with an instance of your server won't receive QTcpServer's "version" of the signal, only the one you emit directly from your sub-class.

Here's a proof of concept: class A is the QTcpServer, foo is the signal you're trying to "hijack", bar is just another (hypothetical) of QTcpServer's signals you don't 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. Notice that it redefines signal foo, but doesn't do anything to 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, connects the signals/slots from a B instance exactly like it would for an A instance.

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

Here's the output from constructing a C:

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

... and nothing else. A's emit foo(1) isn't connected to C's foo slot, it will never arrive to C. A's emit bar(1) worked as expected, that signal is untouched.

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

Answer2:

To be a true drop in replacement, you probably will need to edit the actual source of Qt, because you normally can't reimplement any Private class calls.

If you are the only one using the replacement one, and you control the classes that connect to the newConnection signal...

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

EDIT: After a bit of digging, I found an option of to reconnect a signal:

<a href="http://qt-project.org/forums/viewthread/6820" rel="nofollow">http://qt-project.org/forums/viewthread/6820</a>

Basically, you reimplement QObject::connect, and then you keep track of the connections and process them the way you need to. So in this case, you would keep a list of all the connections of the signal newConnection and keep it in a list so when you disconnect it you could reconnect it. Be sure to call QObject::connect at the end of the reimplementation.

Another option when going this route would be to go and just reroute the connections there. When a connection is requested from newConnection, move it there to myNewConnection.

Hope that helps.

Answer3:

A dirty hack would be to very briefly block the signals from QTcpServer. Since you know that newConnection() will be emitted right after you return from SslServer::incomingConnection(), call this->blockSignals(true); just before you return. That will prevent newConnection() from invoking any slots it is connected to.

To make sure you receive subsequent signals, unblock signals as soon as you can. I suppose the earliest time available would be right when control goes back to the event loop, so a QTimer::singleShot could do it.

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 downside of that is that you will lose every signal that could legitimately be emited between incomingConnection() and unblockSignals(). Like I said, it's a dirty hack.

Recommend

  • Python Socket - Multiple Clients
  • Does an Android Device have to be rooted in order to telnet to it?
  • Qt C++ Creating toolbar
  • What is the cost of deleting a value from a hashtable?
  • Exit QThread when GUI Application exits
  • Storm Topology does not start with parallelism hint of 1200
  • How can I set column in row on the card group bootstrap vue?
  • Display coordinates in pyqtgraph?
  • Angular 2 configuration issues with Azure Slot deployments
  • How to start new graphics window AND/or graphics page in R
  • How to avoid insn beeing scheduled into a delay slot
  • Get all Time slots (1 hour gap) by giving two times(start and end) in android
  • Jquery AJAX post to update database
  • Recive different type of objects on the same TCP socket
  • Qt table widget, button to delete row
  • sql join on two fields in one table
  • Sort by embedded document
  • Resizable jQuery for booking rooms
  • How to have a QTextBrowser to display contents of a QTextEdit?
  • Prolog: Decrementing variable in argument
  • Method to get to the middle of the file for Binary Search
  • How to load files to local file system with vibed?
  • java.net.Socket > outputStream > BufferedOutputStream flush() confirmation
  • Qt: closing modal dialog closes the program
  • c#.NET USB device persistent identifier
  • How to send control C to Mac Terminal using python?
  • How to access recipient on sent messages page with mailboxer
  • How to stamp out template in self contained custom elements with vanilla js?
  • triggering user space with kernel
  • Click on button in another program - FindWindow, C#
  • Intel-64 and ia32 atomic operations acquire-release semantics and GCC 5+
  • c# open webrowser in many tab
  • Force show.bind execution
  • How to Cache Real-time Data?
  • Rearranging Cells in UITableView Bug & Saving Changes
  • AT Commands to Send SMS not working in Windows 8.1
  • How get height of the a view with gone visibility and height defined as wrap_content in xml?
  • FormattedException instead of throw new Exception(string.Format(…)) in .NET
  • Sorting a 2D array using the second column C++
  • java string with new operator and a literal