Ich bin daran interessiert, wie QTcpServer hinter den Kulissen in Bezug auf Threads und Blockierungen arbeitet. QTcpServer
hat eine listen()
-Methode, die sofort zurückkehrt. Wenn die Überwachung erfolgreich gestartet wurde, gibt der Server das Signal newConnection()
aus. Ich bin daran interessiert, wie der Server zuhören (ist es auf den Haupt-Thread), wenn die Methode listen()
zurückgegeben hat. Das übliche Beispiel einer Konsolenanwendung mit einem QTcpServer ist etwa so:
Ist QTcpServer
abhängig von einem QCoreApplication
(oder vielleicht einem QRunLoop
), das vorhanden ist und ausgeführt wird, um Netzwerkereignisse zu empfangen. Kann es richtig funktionieren, ohne dass ein QCoreApplication::exec()
aufgerufen wird?
Ich habe den Quellcode der Module QtCore
und QtNetwork
durchgegangen.
Interessanterweise kann QTcpServer
in zwei Modi arbeiten: synchron und asynchron .
Im synchronen Modus nach dem Aufruf von listen()
kann der Aufrufer waitForNewConnection()
aufrufen, was eine blockierende Methode ist (der Thread wird schlafen, bis sich jemand mit dem empfangenden Port verbindet). Auf diese Weise kann QTcpServer
in einem Thread ohne Ereignisschleife arbeiten.
Im Modus asynchron wird QTcpServer
das Signal newConnection()
ausgeben, wenn eine neue Verbindung akzeptiert wurde. Dazu muss jedoch eine Ereignisschleife ausgeführt werden. Dem QCoreApplication
liegen die QEventLoop
und QAbstractEventDispatcher
zugrunde (eine abstrakte Klasse, der konkrete Typ ist abhängig vom Betriebssystem, zB QEventDispatcherUNIX
). Dieser Event-Dispatcher kann Bedingungen auf Sockets überwachen (dargestellt durch Dateideskriptoren). Es hat eine Methode registerSocketNotifier(QSocketNotifier*)
. Diese Methode wird vom Konstruktor der Klasse QSocketNotifier
aufgerufen, wobei QTcpServer
eine Instanz jedes Mal erstellt, wenn listen()
aufgerufen wird. Der einzige Systemaufruf, der aufgerufen wird, wenn QTcpServer::listen()
aufgerufen wird, ist natürlich listen()
, das einfach sofort zurückkehrt. Die eigentliche Magie tritt auf, wenn die Ereignisschleife beginnt. Die Ereignisschleife (mit dem Dispatcher) überwacht, ob eine bestimmte Bedingung für Sockets vorliegt, die registriert wurden. Sie ruft den Systemaufruf select()
auf, der einen oder mehrere Dateideskriptoren für bestimmte Bedingungen (vom Kernel) überwacht (wenn Daten gelesen werden sollen, Daten geschrieben werden können oder wenn Ein Fehler ist aufgetreten). Der Aufruf kann den Thread blockieren, bis die Bedingungen für Sockets erfüllt sind, oder er kann nach einiger Zeit zurückkehren und die Bedingungen für Sockets wurden nicht erfüllt. Ich bin mir nicht sicher, ob Qt select()
mit einer gelieferten Wartezeit oder ohne (unbegrenzt zu blockieren) anruft, ich denke, dass es in einer komplizierten Weise und veränderbar ist. Wenn die Bedingung für den Socket schließlich erfüllt ist, wird der Event-Dispatcher die QSocketNotifier
für diesen Socket benachrichtigen, der wiederum QTcpServer
benachrichtigt, wer den Socket abgehört, der die Verbindung akzeptiert und sendet das Signal newConnection()
.
Also ruft das QTcpServer
nicht selbst das Event-Loop / Socket-Monitoring-System auf, sondern es hängt von ihm über das QSocketNotifier
ab, das es für das asynchrone Empfangen von Verbindungen verwendet.
Wenn die synchrone Methode waitForNewConnection()
aufgerufen wird, umgeht sie einfach alle QSocketNotifier
stuff und ruft accept()
auf, was den Thread blockiert, bis eine Verbindung eingeht.
Tags und Links qt qtcpserver