2012-07-26 9 views
8

Sono interessato a come QTcpServer funziona dietro le quinte riguardanti i thread e il blocco. QTcpServer ha un metodo listen() che restituisce immediatamente. Se l'ascolto è iniziato correttamente, il server emetterà il segnale newConnection(). Sono interessato a come il server sta ascoltando (è sul thread principale) quando è stato restituito il metodo listen(). Il solito esempio di un'applicazione console con un QTcpServer è qualcosa di simile:In che modo QTcpServer sta veramente ascoltando le connessioni

//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... 
} 

È QTcpServer dipendente da una QCoreApplication (o forse un QRunLoop) esistente e in esecuzione a fianco per ricevere gli eventi di rete. Funziona correttamente senza chiamare QCoreApplication::exec()?

risposta

16

Ho eseguito il drill del codice sorgente dei moduli QtCore e QtNetwork.

Aperantly, QTcpServer può funzionare in due modalità: sincrono e asincrono.

In modalità sincrona dopo aver chiamato listen() il chiamante può chiamare waitForNewConnection() che è un metodo di blocco (il thread dormirà fino a quando qualcuno si connette alla porta di ascolto). In questo modo QTcpServer può funzionare in una discussione senza un ciclo di eventi.

In asincrona modalità QTcpServer emetterà il segnale newConnection() quando una nuova connessione è stata accettata. Ma per essere in grado di farlo ci deve essere un run-run del ciclo degli eventi. Sotto il QCoreApplication sono QEventLoop e QAbstractEventDispatcher (una classe astratta, il tipo di calcestruzzo dipende dal sistema operativo, ad esempio QEventDispatcherUNIX). Questo dispatcher di eventi può monitorare le condizioni sui socket (rappresentati dai descrittori di file). Ha un metodo registerSocketNotifier(QSocketNotifier*). Questo metodo viene chiamato dal costruttore della classe QSocketNotifier, che QTcpServer crea un'istanza di ogni volta che viene chiamato listen(). L'unica chiamata di sistema che viene chiamata quando viene invocato lo QTcpServer::listen() è, naturalmente, listen() che viene restituita immediatamente, tutta la vera magia si verifica quando il ciclo degli eventi inizia a funzionare. Il ciclo degli eventi (utilizzando il dispatcher) monitorerà se c'è una determinata condizione sui socket che sono stati registrati. Chiama la chiamata di sistema select() che riceve uno o più descrittori di file da monitorare (dal kernel) per determinate condizioni (se sono presenti dati da leggere, se è possibile scrivere dati o se si è verificato un errore). La chiamata può bloccare il thread finché non sono soddisfatte le condizioni sui socket, oppure può tornare dopo che è trascorso un po 'di tempo e non sono state soddisfatte le condizioni sulle prese. Non sono sicuro che Qt stia chiamando select() con un tempo di attesa fornito o meno (da bloccare indefinitamente), penso che sia determinato in un modo complicato e mutevole. Quindi, quando finalmente la condizione sul socket è stata soddisfatta, il dispatcher dell'evento comunicherà allo QSocketNotifier per quel socket che, in terna, comunicherà allo QTcpServer chi sta ascoltando il socket, che accetterà la connessione ed emetterà il segnale newConnection().


Quindi la QTcpServer non si chiama nel/sistema presa-monitoraggio evento-loop, ma dipende da esso tramite l'QSocketNotifier che utilizza per recieving asincrona di connessioni.

Quando viene chiamato il metodo sincrono waitForNewConnection(), bypassa tutto il materiale QSocketNotifier e chiama accept() che blocca il thread fino a quando non vi è una connessione in ingresso.

+0

Molto bello. Ora capisco perché chiamare ascolti prima di eseguire il ciclo di eventi delle applicazioni in Qt (io uso PyQt) ha comportato il mancato ottenimento di un segnale 'newConnection'. Grazie per aver scavato. – Trilarion

0

La maggior parte di Qt del "dietro le quinte" funzionalità accade all'interno ciclo di eventi principale di QCoreApplication: segnali/slot, timer, ecc
Un esempio potrebbe essere JavaScript - si associa un evento, ma ciclo di eventi viene elaborato dal browser.