Программирование сетей с Qt
Модуль сетей Qt предлагает классы, позволяющие писать TCP/IP-клиенты и серверы. Он предлагает классы низкого уровня, такие как QTcpSocket, QTcpServer и QUdpSocket, представляющие концепции низкого уровня сети, и классы высокого уровня, такие как QNetworkRequest, QNetworkReply и QNetworkAccessManager, для выполнения сетевых операций с помощью общих протоколов. Он также предлагает классы, такие как QNetworkConfiguration, QNetworkConfigurationManager и QNetworkSession, которые реализуют управление подключением.
Классы Qt для программирования сетей
Страница классов Qt Network на C++ содержит список классов C++ в Qt Network.
Операции с сетями высокого уровня для HTTP и FTP
API доступа к сетям — это набор классов для выполнения общих сетевых операций. API предоставляет уровень абстракции над конкретными операциями и протоколами (например, получение и отправка данных по HTTP), и предоставляет только классы, функции и сигналы для общих или концепций высокого уровня.
Запросы в сеть представляются классом QNetworkRequest, который также служит общим контейнером для информации, связанной с запросом, например, любой информацией заголовка и используемым шифрованием. URL-адрес, указанный при создании объекта запроса, определяет протокол, используемый для запроса. В настоящее время поддерживаются URL-адреса HTTP, FTP и локальных файлов для загрузки и скачивания.
Координацию сетевых операций выполняет класс 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...() не будет заблокирована. Если используется в потоке 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 с помощью 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; см. документацию класса для получения подробностей.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.15/qtnetwork-programming.html