2012-07-26 10 views
8

Jestem zainteresowany tym, jak QTcpServer działa za kulisami w odniesieniu do wątków i blokowania. QTcpServer ma metodę listen(), która natychmiast powraca. Jeśli słuchanie rozpocznie się pomyślnie, serwer wyemituje sygnał newConnection(). Interesuje mnie, w jaki sposób serwer nasłuchuje (znajduje się w głównym wątku) po zwróceniu metody listen(). Zazwyczaj Przykładem aplikacji konsoli z QTcpServer jest coś takiego:Jak naprawdę QTcpServer nasłuchuje na połączeniach

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

Czy QTcpServer zależne od QCoreApplication (a może QRunLoop) istniejące i działa do recive zdarzenia sieciowe. Czy może działać poprawnie bez wywoływania?

Odpowiedz

16

Przeszukujełem kod źródłowy modułów QtCore i QtNetwork.

Aperantly, QTcpServer może pracować w dwóch trybach: synchroniczny i asynchroniczne.

W trybie synchronicznym po wywołaniu listen() rozmówcy mogą dzwonić waitForNewConnection() który jest metoda blokująca (nitka będzie spać dopóki ktoś podłącza się do portu słuchanie). W ten sposób QTcpServer może pracować w wątku bez pętli zdarzeń.

W trybie asynchronicznym tryb QTcpServer będzie emitować sygnał newConnection() po zaakceptowaniu nowego połączenia. Ale aby to zrobić, musi być uruchomiona pętla zdarzeń. U podstaw QCoreApplicationQEventLoop i QAbstractEventDispatcher (klasa abstrakcyjna, typ betonu zależy od systemu operacyjnego, na przykład QEventDispatcherUNIX). Ten moduł rozsyłający zdarzenia może monitorować warunki na gniazdach (reprezentowane przez deskryptory plików). Ma metodę registerSocketNotifier(QSocketNotifier*). Ta metoda jest wywoływana przez konstruktor klasy QSocketNotifier, która QTcpServer tworzy instancję za każdym razem, gdy wywoływana jest nazwa listen(). Jedynym wywołaniem systemowym wywoływanym podczas wywoływania QTcpServer::listen() jest oczywiście listen(), który po prostu natychmiast wraca, cała prawdziwa magia ma miejsce, gdy pętla zdarzeń zaczyna działać. Pętla zdarzeń (za pomocą modułu rozsyłającego) monitoruje, czy na zarejestrowanych gniazdach występuje pewien stan. Wywołuje on wywołanie systemowe, które odbiera jeden lub więcej deskryptorów plików, które mają być monitorowane (przez jądro) pod pewnymi warunkami (jeśli istnieją dane do odczytania, czy dane mogą zostać zapisane lub wystąpił błąd). Połączenie może zablokować wątek do momentu spełnienia warunków na gniazdach lub może powrócić po upływie pewnego czasu, a warunki na gniazdach nie zostały spełnione. Nie jestem pewien, czy Qt wywołuje select() z czasem oczekiwania dostarczonym lub bez (do blokowania w nieskończoność), myślę, że to jest określone w pewien skomplikowany sposób i zmienne. Kiedy wreszcie warunek na gnieździe zostanie spełniony, program rozsyłający zdarzenia powiadomi o tym QSocketNotifier dla tego gniazda, które powiadomi o tym QTcpServer, który nasłuchuje gniazda, które zaakceptuje połączenie i wyśle ​​sygnał newConnection().


Więc QTcpServer sama nie zadzwonić do pętli zdarzeń/systemu monitorowania gniazda, ale to zależy od niego poprzez QSocketNotifier którego używa do asynchronicznego ODBIORCZA połączeń.

Po wywołaniu metody synchronicznej waitForNewConnection() po prostu omija wszystkie rzeczy QSocketNotifier i wywołuje accept(), która blokuje wątek, dopóki nie pojawi się połączenie przychodzące.

+0

Bardzo ładne. Teraz rozumiem, dlaczego wywoływanie nasłuchu przed wykonaniem pętli zdarzeń aplikacji w Qt (używam PyQt) spowodowało, że nie otrzymałem sygnału 'newConnection'. Dzięki za kopanie. – Trilarion

0

Większość funkcji Qt "za kulisami" dzieje się wewnątrz głównej pętli zdarzeń QCoreApplication: sygnały/gniazda, timery itp.
Przykładem może być JavaScript - wiążemy zdarzenie, ale pętla zdarzeń jest przetwarzana przez przeglądarkę.