I am interested in how QTcpServer works behind the scenes regarding threads and blocking. QTcpServer
has a listen()
method which returns immediately. If listening started successfully the server will emit the signal newConnection()
. I'm interested into how is the server listening (is it on the main thread) when the listen()
method has returned. The usual example of a console application with a QTcpServer is something like this:
//main.cpp
int main(int argc, char* argv[])
{
QCoreApplication app;
MyServer server;
app.exec();
}
//MyServer.cpp
MyServer::MyServer(QObject *parent) : QObject(parent)
{
this->server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()), this, SLOT(on_newConnection()));
if (!server->listen(QHostAddress::Any, 1234))
//do something in case of error
}
void MyServer::on_newConnection()
{
QTcpSocket* socket = server->nextPendingConnection();
//do some communication...
}
Is QTcpServer
dependent upon a QCoreApplication
(or maybe a QRunLoop
) existing and running to receive network events. Can it work properly without a QCoreApplication::exec()
being called?
I've been drilling through the source code of the QtCore
and QtNetwork
modules.
Aperantly, QTcpServer
can work in two modes: synchronous and asynchronous.
In synchronous mode after calling listen()
the caller can call waitForNewConnection()
which is a blocking method (the thread will sleep until someone connects to the listening port). This way QTcpServer
can work in a thread without an event loop.
In asynchronous mode QTcpServer
will emit the newConnection()
signal when a new connection was accepted. But to be able to do this there must be an event loop runing. Underlying the QCoreApplication
are the QEventLoop
and QAbstractEventDispatcher
(an abstract class, concrete type is dependent on the OS, for example QEventDispatcherUNIX
). This event dispatcher can monitor for conditions on sockets (represented by file descriptors). It has a method registerSocketNotifier(QSocketNotifier*)
. This method is called by the constructor of the QSocketNotifier
class, which QTcpServer
creates an instance of every time listen()
is called. The only system call that is called when the QTcpServer::listen()
is invoked is, of course, listen()
which just returns immediately, all the real magic happens when the event loop starts running. The event loop (using the dispatcher) will monitor if there is a certain condition on sockets that have been registered. It calls the select()
system call which receives one or more file descriptors to be monitored (by the kernel) for certain conditions (if there is data to be read, if data can be written, or if an error has occurred). The call can block the thread until the conditions on sockets are met, or it can return after some amount of time passes and the conditions on sockets were not met. I am not sure if Qt is calling select()
with a waiting time supplied or without (to block indefinitely), I think it is determined in some complicated way and changeable. So when finally the condition on socket has been met, the event dispatcher will notify the QSocketNotifier
for that socket, which will, in tern, notify the QTcpServer
who is listening to the socket, which will accept the connection, and emit the newConnection()
signal.
So the QTcpServer
does not itself call into the event-loop/socket-monitoring system, but it is dependent upon it via the QSocketNotifier
which it uses for asynchronous recieving of connections.
When synchronous method waitForNewConnection()
is called it just bypasses all the QSocketNotifier
stuff and calls accept()
which blocks the thread until there is an incoming connection.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With