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