Как QTcpServer на самом деле прослушивает соединения
-
11-12-2019 - |
Вопрос
Меня интересует, как 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()
зовут?
Решение
Я изучал исходный код QtCore
и QtNetwork
модули.
Кстати, 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's "за сценами" функциональность происходит внутри основного цикла событий QcoReApplication: сигналы / слоты, таймеры и т. Д.
Примером будет JavaScript - привязываете событие, но цикл событий обрабатывается браузером.