Программирование сетей с помощью 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, указанный при построении объекта запроса, определяет протокол, используемый для запроса. В настоящее время поддерживаются HTTP, FTP и локальные URL-адреса файлов для загрузки и скачивания.
Координация сетевых операций выполняется классом QNetworkAccessManager. После создания запроса этот класс используется для его отправки и выдачи сигналов, сообщающих о его прогрессе. Менеджер также координирует использование cookies для хранения данных на клиенте, запросы аутентификации и использование прокси-серверов.
Ответы на сетевые запросы представлены классом 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...() блокирует поток. Если это используется в потоке пользовательского интерфейса, это может заморозить пользовательский интерфейс приложения. По этой причине мы рекомендуем использовать синхронные сокеты только в потоках, не связанных с пользовательским интерфейсом. При синхронном использовании 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(), которая принимает имя хоста в качестве аргумента и возвращает результаты. В этом случае поиск имени выполняется в том же потоке, что и вызывающая функция. Эта перегрузка полезна для приложений, не связанных с графическим интерфейсом, или для выполнения поиска имён в отдельном потоке, не связанном с графическим интерфейсом. (Вызов этой функции в потоке графического интерфейса может привести к зависанию пользовательского интерфейса, пока функция блокирована для выполнения поиска.)
Поддержка сетевых прокси-серверов
Связь с сетью с помощью 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.12/qtnetwork-programming.html