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
AD

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

  • Результат:50бали,
  • Рейтинг балів-4
m
  • molni99
  • 26 жовтня 2024 р. 08:37

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

  • Результат:80бали,
  • Рейтинг балів4
m
  • molni99
  • 26 жовтня 2024 р. 08:29

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

  • Результат:20бали,
  • Рейтинг балів-10
Останні коментарі
ИМ
Игорь Максимов22 листопада 2024 р. 19:51
Django - Підручник 017. Налаштуйте сторінку входу до Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 жовтня 2024 р. 21:37
Django - Урок 064. Як написати розширення для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 жовтня 2024 р. 15:19
Читалка файлів fb3 на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов05 жовтня 2024 р. 14:51
Django - Урок 064. Як написати розширення для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas505 липня 2024 р. 18:02
QML - Урок 016. База даних SQLite та робота з нею в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Тепер обговоріть на форумі
Evgenii Legotckoi
Evgenii Legotckoi24 червня 2024 р. 22:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 листопада 2024 р. 14:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject04 червня 2022 р. 10:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 жовтня 2024 р. 16:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

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