Как QTcpServer действительно прослушивает соединения
Меня интересует, как QTcpServer работает за кулисами относительно потоков и блокировок. QTcpServer
имеет listen()
метод, который возвращается немедленно. Если прослушивание началось успешно, сервер подаст сигнал newConnection()
, Меня интересует, как сервер слушает (находится ли он в главном потоке), когда listen()
метод вернулся. Обычный пример консольного приложения с QTcpServer выглядит примерно так:
//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
зависит от QCoreApplication
(или может быть QRunLoop
) существующие и работающие для получения сетевых событий. Может ли он работать правильно без QCoreApplication::exec()
вызывается?
2 ответа
Я бурю через исходный код QtCore
а также QtNetwork
модули.
Aperantly, QTcpServer
может работать в двух режимах: синхронном и асинхронном.
В синхронном режиме после звонка listen()
звонящий может позвонить waitForNewConnection()
который является методом блокировки (поток будет спать, пока кто-то не подключится к порту прослушивания). Сюда QTcpServer
может работать в потоке без цикла событий.
В асинхронном режиме QTcpServer
будет излучать newConnection()
сигнал, когда новое соединение было принято. Но чтобы сделать это, должен быть запущен цикл обработки событий. В основе QCoreApplication
являются QEventLoop
а также QAbstractEventDispatcher
(абстрактный класс, конкретный тип зависит от ОС, например QEventDispatcherUNIX
). Этот диспетчер событий может отслеживать условия в сокетах (представленные дескрипторами файлов). У него есть метод registerSocketNotifier(QSocketNotifier*)
, Этот метод вызывается конструктором QSocketNotifier
класс, который QTcpServer
создает экземпляр каждого времени listen()
называется. Единственный системный вызов, который вызывается, когда QTcpServer::listen()
вызывается, конечно, listen()
который просто возвращается немедленно, вся настоящая магия происходит, когда запускается цикл обработки событий. Цикл обработки событий (с использованием диспетчера) будет отслеживать наличие определенных условий на зарегистрированных сокетах. Это вызывает select()
системный вызов, который получает один или несколько файловых дескрипторов для мониторинга (ядром) для определенных условий (если есть данные для чтения, если данные могут быть записаны или произошла ошибка). Вызов может блокировать поток до тех пор, пока не будут выполнены условия для сокетов, или он может вернуться через некоторое время, а условия для сокетов не будут выполнены. Я не уверен, что Qt звонит select()
с предоставленным временем ожидания или без него (блокировать на неопределенный срок), я думаю, что оно определяется каким-то сложным образом и может изменяться. Поэтому, когда, наконец, условие для сокета будет выполнено, диспетчер событий уведомит QSocketNotifier
для этого сокета, который, в свою очередь, уведомит QTcpServer
кто слушает сокет, который примет соединение и испустит newConnection()
сигнал.
Итак QTcpServer
сам по себе не вызывает систему мониторинга событий / циклов, но зависит от нее через QSocketNotifier
который он использует для асинхронного получения соединений.
Когда синхронный метод waitForNewConnection()
называется это просто обходит все QSocketNotifier
вещи и звонки accept()
который блокирует поток, пока не будет входящего соединения.
Большая часть функциональности Qt "за кулисами" происходит внутри основного цикла событий QCoreApplication: сигналы / слоты, таймеры и т. Д.
Примером может служить JavaScript - вы связываете событие, но цикл обработки обрабатывается браузером.