Evgenii Legotckoi
Evgenii Legotckoi06 жовтня 2015 р. 13:13

Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt

Відразу хочу засмутити Вас, Дорогий Читачу. Qt не має функціонала для роботи з протоколом ICMP і відповідно доведеться використовувати для цих цілей API цільової операційної системи. Втім, це не дивно. Протокол ICMP є низькорівневим протоколом, і для роботи з ним потрібне використання сирих сокетів, які не реалізовані у Qt .

Але це не є особливою проблемою, оскільки основні цільові платформи мають необхідний API для реалізації ping посилок. Наприклад, Microsoft надає просте використання ICMP протоколу на основі функції IcmpSendEcho.

Опис IcmpSendEcho

Функція IcmpSendEcho відсилає луну запити IPv4 ICMP та повертає відповіді на луну запити. Виклик повертається, коли виходить час очікування або заповнюється буфер відповіді.

DWORD IcmpSendEcho(
  _In_     HANDLE                 IcmpHandle,
  _In_     IPAddr                 DestinationAddress,
  _In_     LPVOID                 RequestData,
  _In_     WORD                   RequestSize,
  _In_opt_ PIP_OPTION_INFORMATION RequestOptions,
  _Out_    LPVOID                 ReplyBuffer,
  _In_     DWORD                  ReplySize,
  _In_     DWORD                  Timeout
);

Параметри

IcmpHandle [в]
Відкритий обробник, який повертається функцією IcmpCreateFile .

адреса призначення [в]
Адреса призначення відлуння запиту IPv4 , задається у вигляді IPAddr структури.

RequestData [в]
Вказівник на буфер, який містить запит, що посилається.

RequestSize [in ]
Розмір, у байтах, буфера даних запиту, який вказує параметр RequestData .

RequestOptions [в, необов'язково]
Вказівник на опції IP заголовка для запиту задається у вигляді IP_OPTION_INFORMATION структури. На 64-бітних платформах задається у вигляді IP_OPTION_INFORMATION32 структури.

Цей параметр може мати значення NULL , якщо параметри IP заголовка не уточнюються.

ReplyBuffer [вихід]
Буфер, що утримує кілька відповідей на відлуння запит. Після повернення буффер містить масив структури ICMP_ECHO_REPLY наступних параметрів і даних для відповіді. Буффер повинен бути великим, щоб містити щонайменше одну ICMP_ECHO_REPLY структуру, а також RequestSize байтів даних .

На 64-бітних платформах після повернення буфер містить масив ICMP_ECHO_REPLY32 структури.

ReplySize [в]
Вказує розмір у байтах для буфера відповіді. Буффер повинен бути великим, щоб містити щонайменше одну ICMP_ECHO_REPLY структуру, а також RequestSize байтів даних . на 64-бітних платформах буффер повинен бути достатньо великим, щоб містити щонайменше одну ICMP_ECHO_REPLY32 структуру та плюс RequestSize байтів даних.

Цей буфер повинен бути також більшим для утримання понад 8 байтів даних (Розмір повідомлення помилки ICMP ).

Час очікування [в]
Час очікування у мілісекундах.

Повертається значення

Функція IcmpSendEcho повертає число ICMP_ECHO_REPLY або ICMP_ECHO_REPLY32 структур, збережених у ReplyBuffer . Статус кожної відповіді міститься у структурі. Якщо значення, що повертається, дорівнює 0, викликається функція GetLastError для додаткової інформації.

Якщо функція зазнає невдачі, за допомогою функції GetLastError поверне код помилки з такими значеннями:

  • ERROR_INSUFFICIENT_BUFFER - Область передаваних даних для системного виклику занадто мала. Помилка повертається, якщо параметр ReplySize вказує на буфер *ReplyBuffer, який занадто малий.
  • ERROR_INVALID_PARAMETER - Неправильний параметр був переданий у функцію. Ця помилка виникає в тому випадку, якщо параметр IcmpHandle містить помилковий обробник. Ця помилка може бути повернена також, якщо параметр ReplySize має значення менше розміру ICMP_ECHO_REPLY або ICMP_ECHO_REPLY32 структур.
  • ERROR_NOT_ENOUGH_MEMORY - Недостатньо пам'яті для завершення операції.
  • ERROR_NOT_SUPPORT - Запит не підтримується. Ця помилка повертається, якщо немає стека IPv4 на локальному комп'ютері.
  • IP_BUF_TOO_SMALL - Розмір ReplyBuffer, вказаний у параметрі ReplySize є занадто малим.
  • Інші - Використовуйте FormatMessage, для того щоб отримати рядкове повідомлення про помилку, що повертається.

Робота з ICMP

Функція IcmpSendEcho надсилає ICMP луна запит за вказаною адресою і повертає кількість пінятих та збережених відповідей у ReplyBuffer . Функція IcmpSendEcho є синхронною функцією та повертає значення після закінчення часу очікування для відповіді. Якщо повертається нуль, для отримання розширеної інформації необхідно викликати функцію GetLastError .

Для того, щоб використовувати цю частину WinAPI, необхідно в .Pro файлі проекту прописати два наступні рядки:

LIBS     += -lws2_32
LIBS     += -liphlpapi

А також підключити такі бібліотеки:

#include "winsock2.h"
#include "iphlpapi.h"
#include "icmpapi.h"

Головне вікно програми

Для того, щоб ознайомитися з протоколом ICMP створимо вікно з наступним інтерфейсом, де відображатиметься відповідь на запити, IP-адресу та кнопка запуску.

віджет.h

Крім оголошення слота від кнопки Ви тут нічого цікавого не побачите, але тим не менш наводжу заголовний файл.

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QDebug>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

widget.cpp

А ось тут найцікавіше. Після натискання кнопки Ми будемо пінгувати обрану нами IP-адресу. Для краси рішення я запровадив валідацію IP-адреси в полі lintEdit.

#include "widget.h"
#include "ui_widget.h"
#include "winsock2.h"
#include "iphlpapi.h"
#include "icmpapi.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    // Производим валидацию вводимых данных IP-адреса
    QString ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])";

    QRegExp ipRegex ("^" + ipRange
                     + "\\." + ipRange
                     + "\\." + ipRange
                     + "\\." + ipRange + "$");

    QRegExpValidator *ipValidator = new QRegExpValidator(ipRegex, this);
    ui->lineEdit->setValidator(ipValidator);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_clicked()
{
    // Объявляем переменные
    HANDLE hIcmpFile;                       // Обработчик
    unsigned long ipaddr = INADDR_NONE;     // Адрес назначения
    DWORD dwRetVal = 0;                     // Количество ответов
    char SendData[32] = "Data Buffer";      // Буффер отсылаемых данных
    LPVOID ReplyBuffer = NULL;              // Буффер ответов
    DWORD ReplySize = 0;                    // Размер буффера ответов

    // Устанавливаем IP-адрес из поля lineEdit
    ipaddr = inet_addr(ui->lineEdit->text().toStdString().c_str());
    hIcmpFile = IcmpCreateFile();   // Создаём обработчик

    // Выделяем память под буффер ответов
    ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
    ReplyBuffer = (VOID*) malloc(ReplySize);

    // Вызываем функцию ICMP эхо запроса
    dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData),
                NULL, ReplyBuffer, ReplySize, 1000);

    // создаём строку, в которою запишем сообщения ответа
    QString strMessage = "";

    if (dwRetVal != 0) {
        // Структура эхо ответа
        PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
        struct in_addr ReplyAddr;
        ReplyAddr.S_un.S_addr = pEchoReply->Address;

        strMessage += "Sent icmp message to " + ui->lineEdit->text() + "\n";
        if (dwRetVal > 1) {
            strMessage += "Received " + QString::number(dwRetVal) + " icmp message responses \n";
            strMessage += "Information from the first response: ";
        }
        else {
            strMessage += "Received " + QString::number(dwRetVal) + " icmp message response \n";
            strMessage += "Information from the first response: ";
        }
            strMessage += "Received from ";
            strMessage += inet_ntoa( ReplyAddr );
            strMessage += "\n";
            strMessage += "Status = " + pEchoReply->Status;
            strMessage += "Roundtrip time = " + QString::number(pEchoReply->RoundTripTime) + " milliseconds \n";
    } else {
        strMessage += "Call to IcmpSendEcho failed.\n";
        strMessage += "IcmpSendEcho returned error: ";
        strMessage += QString::number(GetLastError());
    }

    ui->textEdit->setText(strMessage); // Отображаем информацию о полученных данных
    free(ReplyBuffer); // Освобождаем память
}

Підсумок

В результаті у Вас має вийти програма, яка пінгуватиме обрану Вами IP-адресу.

Демонстрацію роботи програми наведено у відеоуроці.

Відеоурок

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Стабільний хостинг, на якому розміщується соціальна мережа EVILEG. Для проектів на Django радимо VDS хостинг.

Вам це подобається? Поділіться в соціальних мережах!

АК
  • 05 лютого 2024 р. 01:50
  • (відредаговано)

Без строки

#include <QRegularExpressionValidator>

в заголовочном файле не работает валидатор.

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up
d
  • dsfs
  • 26 квітня 2024 р. 11:56

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80бали,
  • Рейтинг балів4
d
  • dsfs
  • 26 квітня 2024 р. 11:45

C++ - Тест 002. Константы

  • Результат:50бали,
  • Рейтинг балів-4
d
  • dsfs
  • 26 квітня 2024 р. 11:35

C++ - Тест 001. Первая программа и типы данных

  • Результат:73бали,
  • Рейтинг балів1
Останні коментарі
k
kmssr09 лютого 2024 р. 02:43
Qt Linux - Урок 001. Автозапуск програми Qt під Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий Кононенко05 лютого 2024 р. 09:50
Qt WinAPI - Урок 007. Робота з ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25 грудня 2023 р. 18:30
Boost - статичне зв&#39;язування в проекті CMake під Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
J
JonnyJo25 грудня 2023 р. 16:38
Boost - статичне зв&#39;язування в проекті CMake під Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
G
Gvozdik19 грудня 2023 р. 05:01
Qt/C++ - Урок 056. Підключення бібліотеки Boost в Qt для компіляторів MinGW і MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
Тепер обговоріть на форумі
G
Gar22 квітня 2024 р. 12:46
Clipboard Как скопировать окно целиком в clipb?
DA
Dr Gangil Academics20 квітня 2024 р. 14:45
Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
a
a_vlasov14 квітня 2024 р. 13:41
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
Павел Дорофеев
Павел Дорофеев14 квітня 2024 р. 09:35
QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
f
fastrex04 квітня 2024 р. 11:47
Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

Слідкуйте за нами в соціальних мережах