Программирование сетей с помощью Qt
Модуль Qt Network предоставляет классы, позволяющие писать TCP/IP-клиенты и серверы. Он предоставляет низкоуровневые классы, такие как QTcpSocket, QTcpServer и QUdpSocket, представляющие низкоуровневые сетевые концепции, и высокоуровневые классы, такие как QNetworkRequest, QNetworkReply и QNetworkAccessManager, для выполнения сетевых операций с использованием общих протоколов. Он также предлагает классы, такие как QNetworkConfiguration, QNetworkConfigurationManager и QNetworkSession, которые реализуют управление носителем.
Классы Qt для программирования сетей
Страница Классы C++ Qt Network содержит список классов C++ в Qt Network.
Высокоуровневые сетевые операции для HTTP и FTP
API доступа к сети — это набор классов для выполнения общих сетевых операций. API предоставляет абстракционный слой над специфическими операциями и протоколами (например, получение и отправка данных по HTTP) и предоставляет только классы, функции и сигналы для общих или высокоуровневых концепций.
Сетевые запросы представлены классом QNetworkRequest, который также служит общим контейнером для информации, связанной с запросом, такой как любая информация в заголовке и используемое шифрование. URL, указанный при создании объекта запроса, определяет используемый протокол для запроса. В настоящее время поддерживаются URL HTTP, FTP и локальные файлы для загрузки и скачивания.
Координацию сетевых операций выполняет класс QNetworkAccessManager. После создания запроса этот класс используется для его отправки и выдачи сигналов для отслеживания его прогресса. Менеджер также координирует использование cookie для хранения данных на клиенте, запросы аутентификации и использование прокси.
Ответы на сетевые запросы представлены классом 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.14/qtnetwork-programming.html