Программирование сетевых приложений с Qt
Модуль Qt Network предоставляет классы, позволяющие создавать TCP/IP-клиенты и серверы. Он предлагает классы низкого уровня, такие как QTcpSocket, QTcpServer и QUdpSocket, представляющие низкоуровневые сетевые понятия, а также классы высокого уровня, такие как QNetworkRequest, QNetworkReply и QNetworkAccessManager, для выполнения сетевых операций с использованием распространенных протоколов. Он также предоставляет классы, такие как QNetworkConfiguration, QNetworkConfigurationManager и QNetworkSession, которые реализуют управление носителем.
Классы Qt для сетевого программирования
Страница C++-классов Qt Network содержит список C++-классов в модуле Qt Network.
Операции на высоком уровне для HTTP и FTP
Network Access API — это набор классов для выполнения распространенных сетевых операций. API предоставляет абстракционный слой поверх конкретных операций и протоколов (например, получение и отправка данных по HTTP) и раскрывает только классы, функции и сигналы для общих или высокоуровневых понятий.
Сеть запросы представлены классом QNetworkRequest, который также действует как общий контейнер для информации, связанной с запросом, такой как заголовки и используемое шифрование. URL, указанный при создании объекта запроса, определяет используемый протокол для запроса. В настоящее время поддерживаются URL-адреса HTTP, FTP и локальных файлов для загрузки и скачивания.
Координацию сетевых операций выполняет класс QNetworkAccessManager. После создания запроса этот класс используется для его отправки и выдачи сигналов для отслеживания его выполнения. Менеджер также координирует использование куки для хранения данных на клиенте, запросы аутентификации и использование прокси-серверов.
Ответы на сетевые запросы представлены классом QNetworkReply, создаваемым классом QNetworkAccessManager при отправке запроса. Сигналы, предоставляемые классом QNetworkReply, можно использовать для отслеживания каждого ответа в отдельности, или разработчики могут выбрать использование сигналов менеджера для этой цели и отказаться от ссылок на ответы. Поскольку QNetworkReply является подклассом QIODevice, ответы могут обрабатываться синхронно или асинхронно; то есть как блокирующие или неблокирующие операции.
Каждое приложение или библиотека может создавать один или несколько экземпляров QNetworkAccessManager для обработки сетевого взаимодействия.
Использование TCP с QTcpSocket и QTcpServer
TCP (Transmission Control Protocol) — это сетевой протокол низкого уровня, используемый большинством интернет-протоколов, включая HTTP и FTP, для передачи данных. Это надежный, ориентированный на потоки, ориентированный на соединение транспортный протокол. Он особенно подходит для непрерывной передачи данных.
Класс QTcpSocket предоставляет интерфейс для TCP. Вы можете использовать QTcpSocket для реализации стандартных сетевых протоколов, таких как POP3, SMTP и NNTP, а также пользовательских протоколов.
Для начала передачи данных необходимо установить TCP-соединение с удаленным хостом и портом. После установления соединения IP-адрес и порт удаленного узла доступны через QTcpSocket::peerAddress() и QTcpSocket::peerPort(). В любой момент удаленный узел может разорвать соединение, и передача данных немедленно остановится.
QTcpSocket работает асинхронно и выдает сигналы для отчета об изменениях статуса и ошибках, как и QNetworkAccessManager. Он полагается на цикл событий для обнаружения входящих данных и автоматического сброса исходящих данных. Вы можете записать данные в сокет с помощью QTcpSocket::write(), а прочитать данные с помощью QTcpSocket::read(). QTcpSocket представляет два независимых потока данных: один для чтения и один для записи.
Поскольку QTcpSocket наследуется от QIODevice, вы можете использовать его с QTextStream и QDataStream. При чтении из QTcpSocket необходимо убедиться, что достаточно данных, вызвав QTcpSocket::bytesAvailable() предварительно.
Если вам необходимо обрабатывать входящие TCP-соединения (например, в приложении-сервере), используйте класс QTcpServer. Вызовите QTcpServer::listen() для настройки сервера и подключитесь к сигналу QTcpServer::newConnection(), который вызывается каждый раз при подключении клиента. В вашем слоте вызовите QTcpServer::nextPendingConnection() для принятия соединения и использования возвращённого QTcpSocket для связи с клиентом.
Хотя большинство его функций работают асинхронно, возможно использование QTcpSocket синхронно (т.е. блокирующее). Для получения блокирующего поведения вызовите функции waitFor...() QTcpSocket; они приостанавливают вызывающую нить до тех пор, пока не будет выпущен сигнал. Например, после вызова неблокирующей функции QTcpSocket::connectToHost() вызовите QTcpSocket::waitForConnected(), чтобы заблокировать нить до тех пор, пока не будет выпущен сигнал connected().
Синхронные сокеты часто приводят к коду с более простым потоком управления. Основным недостатком подхода waitFor...() является то, что события не будут обрабатываться во время блокирования функции waitFor...(). Если используется в потоке GUI, это может заморозить пользовательский интерфейс приложения. По этой причине рекомендуется использовать синхронные сокеты только в не-GUI потоках. При синхронном использовании QTcpSocket не требует цикла событий.
Примеры Fortune Client и Fortune Server показывают, как использовать QTcpSocket и QTcpServer для написания TCP-клиент-серверных приложений. Смотрите также пример Blocking Fortune Client для примера использования синхронного QTcpSocket в отдельном потоке (без использования цикла событий) и Threaded Fortune Server для примера многопоточного TCP-сервера с одним потоком на активного клиента.
Использование UDP с QUdpSocket
UDP (User Datagram Protocol) — это легкий, ненадежный, ориентированный на дейтаграммы, бессоединительный протокол. Он может быть использован, когда надежность не имеет большого значения. Например, сервер, который сообщает текущее время, может выбрать UDP. Если дейтаграмма с текущим временем потеряна, клиент может просто сделать новый запрос.
Класс QUdpSocket позволяет отправлять и получать UDP-дейтаграммы. Он наследуется от QAbstractSocket и, следовательно, разделяет большую часть интерфейса QTcpSocket. Основное различие заключается в том, что QUdpSocket передает данные в виде дейтаграмм, а не непрерывного потока данных. Короче говоря, дейтаграмма — это пакет данных ограниченного размера (обычно меньше 512 байт), содержащий IP-адрес и порт отправителя и получателя дейтаграммы, а также передаваемые данные.
QUdpSocket поддерживает IPv4-вещание. Вещание часто используется для реализации протоколов обнаружения сети, таких как поиск узла в сети с наибольшим свободным объемом жесткого диска. Один узел передает дейтаграмму в сеть, которую получают все остальные узлы. Каждый узел, получивший запрос, отправляет ответ отправителю со своим текущим объемом свободного места на диске. Инициатор ожидает ответов от всех узлов и может выбрать сервер с наибольшим свободным объемом для хранения данных. Чтобы отправить дейтаграмму по вещанию, просто отправьте её по специальному адресу QHostAddress::Broadcast (255.255.255.255) или по широковещательному адресу вашей локальной сети.
QUdpSocket::bind() подготавливает сокет для приема входящих дейтаграмм, так же, как QTcpServer::listen() для TCP-серверов. При поступлении одной или нескольких дейтаграмм QUdpSocket выдает сигнал readyRead(). Вызовите QUdpSocket::readDatagram() для чтения дейтаграммы.
Примеры Broadcast Sender и Broadcast Receiver демонстрируют, как создать UDP-отправителя и UDP-получателя с помощью Qt.
QUdpSocket также поддерживает многоадресную рассылку. Примеры Multicast Sender и Multicast Receiver показывают, как использовать UDP-клиенты многоадресной рассылки.
Разрешение имен хостов с помощью QHostInfo
Перед установлением сетевого соединения QTcpSocket и QUdpSocket выполняют поиск имени, преобразуя имя хоста, к которому вы подключаетесь, в IP-адрес. Эта операция обычно выполняется с помощью протокола DNS (Domain Name Service).
QHostInfo предоставляет статическую функцию, которая позволяет выполнить такой поиск самостоятельно. Вызвав QHostInfo::lookupHost() с именем хоста, указателем QObject и сигнатурой слота, QHostInfo выполнит поиск имени и вызовет заданный слот, когда результаты будут готовы. Фактический поиск выполняется в отдельном потоке, используя собственные методы операционной системы для выполнения поиска имен.
QHostInfo также предоставляет статическую функцию QHostInfo::fromName(), которая принимает имя хоста в качестве аргумента и возвращает результаты. В этом случае поиск имени выполняется в том же потоке, что и вызывающий. Этот перегруз полезен для приложений, не использующих GUI, или для выполнения поиска имен в отдельном, не-GUI потоке. (Вызов этой функции в потоке GUI может привести к зависанию пользовательского интерфейса во время блокирования функции при выполнении поиска.)
Поддержка сетевых прокси
Связь с сетью в Qt может осуществляться через прокси-серверы, которые направляют или фильтруют сетевой трафик между локальными и удалёнными подключениями.
Индивидуальные прокси-серверы представлены классом QNetworkProxy, который используется для описания и настройки подключения к прокси-серверу. Поддерживаются типы прокси-серверов, работающие на разных уровнях сетевой связи, при этом поддержка SOCKS 5 позволяет проксировать сетевой трафик на низком уровне, а проксирование HTTP и FTP работает на уровне протокола. Дополнительную информацию см. в QNetworkProxy::ProxyType.
Проксирование может быть включено на основе каждого сокета или для всех сетевых коммуникаций в приложении. Недавно открытый сокет может использовать прокси, вызвав функцию QAbstractSocket::setProxy() перед подключением. Включение проксирования для всех последующих подключений сокетов на уровне приложения может быть выполнено с помощью функции QNetworkProxy::setApplicationProxy().
Прокси-фабрики используются для создания политик использования прокси-серверов. QNetworkProxyFactory предоставляет прокси-серверы на основе запросов для определённых типов прокси. Сами запросы закодированы в объектах QNetworkProxyQuery, которые позволяют выбирать прокси-серверы на основе ключевых критериев, таких как назначение прокси-сервера (TCP, UDP, TCP-сервер, запрос URL), локальный порт, удалённый хост и порт, и используемый протокол (HTTP, FTP и т.д.).
QNetworkProxyFactory::proxyForQuery() используется для непосредственного запроса фабрики. Политику проксирования на уровне приложения можно реализовать, передав фабрику в QNetworkProxyFactory::setApplicationProxyFactory(), а пользовательскую политику проксирования можно создать, наследуя класс QNetworkProxyFactory; подробную информацию см. в документации класса.
Поддержка управления носителем
Управление носителем контролирует состояние подключения устройства таким образом, что приложение может запускать или останавливать сетевые интерфейсы и прозрачно перемещаться между точками доступа.
Класс QNetworkConfigurationManager управляет списком сетевых конфигураций, известных устройству. Сетевая конфигурация описывает набор параметров, используемых для запуска сетевого интерфейса, и представлена классом QNetworkConfiguration.
Сетевой интерфейс запускается путём открытия QNetworkSession на основе заданной сетевой конфигурации. В большинстве случаев создание сетевой сессии на основе платформенно заданной по умолчанию сетевой конфигурации является подходящим вариантом. По умолчанию сетевая конфигурация возвращается функцией QNetworkConfigurationManager::defaultConfiguration().
На некоторых платформах для выполнения любых сетевых операций приложение должно открыть сетевую сессию. Это можно проверить по наличию флага QNetworkConfigurationManager::NetworkSessionRequired в значении, возвращаемом функцией QNetworkConfigurationManager::capabilities().
См. также Управление носителем.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.13/qtnetwork-programming.html