Wie hört QTcpServer wirklich auf Verbindungen?
-
11-12-2019 - |
Frage
Mich interessiert, wie QTcpServer hinter den Kulissen in Bezug auf Threads und Blockierung funktioniert. QTcpServer
hat ein listen()
Methode, die sofort zurückkehrt.Wenn das Abhören erfolgreich gestartet wurde, sendet der Server das Signal newConnection()
.Mich interessiert, wie der Server zuhört (ist er im Hauptthread), wenn der listen()
Methode ist zurückgekehrt.Das übliche Beispiel einer Konsolenanwendung mit einem QTcpServer sieht etwa so aus:
//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...
}
Ist QTcpServer
abhängig von a QCoreApplication
(oder vielleicht ein QRunLoop
) vorhanden und ausgeführt, um Netzwerkereignisse zu empfangen.Kann es ohne a richtig funktionieren? QCoreApplication::exec()
gerufen werden?
Lösung
Ich habe den Quellcode des durchforstet QtCore
Und QtNetwork
Module.
Offensichtlich, QTcpServer
kann in zwei Modi arbeiten: synchron Und asynchron.
In synchron Modus nach dem Aufruf listen()
Der Anrufer kann anrufen waitForNewConnection()
Dabei handelt es sich um eine Blockierungsmethode (der Thread ruht, bis sich jemand mit dem Überwachungsport verbindet).Hier entlang QTcpServer
kann in einem Thread ohne Ereignisschleife arbeiten.
In asynchron Modus QTcpServer
wird das aussenden newConnection()
Signal, wenn eine neue Verbindung angenommen wurde.Um dies tun zu können, muss jedoch eine Ereignisschleife ausgeführt werden.Dahinter verbirgt sich die QCoreApplication
sind die QEventLoop
Und QAbstractEventDispatcher
(Eine abstrakte Klasse, der konkrete Typ hängt beispielsweise vom Betriebssystem ab QEventDispatcherUNIX
).Dieser Ereignis-Dispatcher kann Bedingungen für Sockets überwachen (dargestellt durch Dateideskriptoren).Es hat eine Methode registerSocketNotifier(QSocketNotifier*)
.Diese Methode wird vom Konstruktor der aufgerufen QSocketNotifier
Klasse, die QTcpServer
Erstellt von jedem Mal eine Instanz listen()
wird genannt.Der einzige Systemaufruf, der aufgerufen wird, wenn die QTcpServer::listen()
aufgerufen wird, ist natürlich listen()
das einfach sofort zurückkehrt, die ganze wahre Magie passiert, wenn die Ereignisschleife zu laufen beginnt.Die Ereignisschleife (mithilfe des Dispatchers) überwacht, ob bei registrierten Sockets eine bestimmte Bedingung vorliegt.Es nennt die select()
Systemaufruf, der einen oder mehrere Dateideskriptoren empfängt, die (vom Kernel) auf bestimmte Bedingungen überwacht werden sollen (ob Daten gelesen werden müssen, ob Daten geschrieben werden können oder ob ein Fehler aufgetreten ist).Der Aufruf kann den Thread blockieren, bis die Bedingungen für Sockets erfüllt sind, oder er kann zurückkehren, nachdem eine gewisse Zeit verstrichen ist und die Bedingungen für Sockets nicht erfüllt wurden.Ich bin nicht sicher, ob Qt anruft select()
Mit oder ohne Wartezeit (auf unbestimmte Zeit blockieren), ich denke, es ist auf eine komplizierte Art und Weise festgelegt und veränderbar.Wenn also schließlich die Bedingung für den Socket erfüllt ist, benachrichtigt der Ereignis-Dispatcher den QSocketNotifier
für diesen Socket, der wiederum den benachrichtigt QTcpServer
Wer hört auf den Socket, der die Verbindung akzeptiert und die ausgibt newConnection()
Signal.
Also die QTcpServer
ruft selbst nicht das Ereignisschleifen-/Socket-Überwachungssystem auf, sondern ist über das darauf angewiesen QSocketNotifier
die es zum asynchronen Empfangen von Verbindungen verwendet.
Bei der synchronen Methode waitForNewConnection()
heißt es umgeht einfach alles QSocketNotifier
Sachen und Anrufe accept()
Dadurch wird der Thread blockiert, bis eine eingehende Verbindung besteht.
Andere Tipps
Die meisten Qt-Funktionen „hinter den Kulissen“ finden innerhalb der Hauptereignisschleife von QCoreApplication statt:Signale/Slots, Timer usw.
Ein Beispiel wäre JavaScript – Sie binden ein Ereignis, aber die Ereignisschleife wird vom Browser verarbeitet.