Evgenii Legotckoi
Қаз. 6, 2015, 11:13 Т.Қ.

Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу

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

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

Описание IcmpSendEcho

Функция IcmpSendEcho отсылает эхо запросы IPv4 ICMP и возвращает ответы на эхо запросы. Вызов возвращается когда выходит время ожидания или заполняется буфер ответа.

  1. DWORD IcmpSendEcho(
  2. _In_ HANDLE IcmpHandle,
  3. _In_ IPAddr DestinationAddress,
  4. _In_ LPVOID RequestData,
  5. _In_ WORD RequestSize,
  6. _In_opt_ PIP_OPTION_INFORMATION RequestOptions,
  7. _Out_ LPVOID ReplyBuffer,
  8. _In_ DWORD ReplySize,
  9. _In_ DWORD Timeout
  10. );

Параметры

IcmpHandle [in]
Открытый обработчик, возвращаемый функцией IcmpCreateFile .

DestinationAddress [in]
Адрес назначения эхо запроса IPv4 , задаётся в виде IPAddr структуры.

RequestData [in]
Указатель на буффер, который содержит посылаемый запрос.

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

RequestOptions [in, optional]
Указатель на опции IP заголовка для запроса, задаётся в виде IP_OPTION_INFORMATION структуры. На 64-битных платформах задаётся в виде IP_OPTION_INFORMATION32 структуры.

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

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

На 64-битных платформах по возвращении буффер содержит массив ICMP_ECHO_REPLY32 структуры.

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

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

Timeout [in]
Время ожидания в миллисекундах.

Возвращаемое значение

Функция 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 файле проекта прописать две следующих строки:

  1. LIBS += -lws2_32
  2. LIBS += -liphlpapi

А также подключить следующие библиотеки:

  1. #include "winsock2.h"
  2. #include "iphlpapi.h"
  3. #include "icmpapi.h"

Главное окно приложения

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

widget.h

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

  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3.  
  4. #include <QWidget>
  5. #include <QDebug>
  6.  
  7. namespace Ui {
  8. class Widget;
  9. }
  10.  
  11. class Widget : public QWidget
  12. {
  13. Q_OBJECT
  14.  
  15. public:
  16. explicit Widget(QWidget *parent = 0);
  17. ~Widget();
  18.  
  19. private slots:
  20. void on_pushButton_clicked();
  21.  
  22. private:
  23. Ui::Widget *ui;
  24. };
  25.  
  26. #endif // WIDGET_H

widget.cpp

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

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include "winsock2.h"
  4. #include "iphlpapi.h"
  5. #include "icmpapi.h"
  6.  
  7. Widget::Widget(QWidget *parent) :
  8. QWidget(parent),
  9. ui(new Ui::Widget)
  10. {
  11. ui->setupUi(this);
  12.  
  13. // Производим валидацию вводимых данных IP-адреса
  14. QString ipRange = "(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])";
  15.  
  16. QRegExp ipRegex ("^" + ipRange
  17. + "\\." + ipRange
  18. + "\\." + ipRange
  19. + "\\." + ipRange + "$");
  20.  
  21. QRegExpValidator *ipValidator = new QRegExpValidator(ipRegex, this);
  22. ui->lineEdit->setValidator(ipValidator);
  23. }
  24.  
  25. Widget::~Widget()
  26. {
  27. delete ui;
  28. }
  29.  
  30. void Widget::on_pushButton_clicked()
  31. {
  32. // Объявляем переменные
  33. HANDLE hIcmpFile; // Обработчик
  34. unsigned long ipaddr = INADDR_NONE; // Адрес назначения
  35. DWORD dwRetVal = 0; // Количество ответов
  36. char SendData[32] = "Data Buffer"; // Буффер отсылаемых данных
  37. LPVOID ReplyBuffer = NULL; // Буффер ответов
  38. DWORD ReplySize = 0; // Размер буффера ответов
  39.  
  40. // Устанавливаем IP-адрес из поля lineEdit
  41. ipaddr = inet_addr(ui->lineEdit->text().toStdString().c_str());
  42. hIcmpFile = IcmpCreateFile(); // Создаём обработчик
  43.  
  44. // Выделяем память под буффер ответов
  45. ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData);
  46. ReplyBuffer = (VOID*) malloc(ReplySize);
  47.  
  48. // Вызываем функцию ICMP эхо запроса
  49. dwRetVal = IcmpSendEcho(hIcmpFile, ipaddr, SendData, sizeof(SendData),
  50. NULL, ReplyBuffer, ReplySize, 1000);
  51.  
  52. // создаём строку, в которою запишем сообщения ответа
  53. QString strMessage = "";
  54.  
  55. if (dwRetVal != 0) {
  56. // Структура эхо ответа
  57. PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
  58. struct in_addr ReplyAddr;
  59. ReplyAddr.S_un.S_addr = pEchoReply->Address;
  60.  
  61. strMessage += "Sent icmp message to " + ui->lineEdit->text() + "\n";
  62. if (dwRetVal > 1) {
  63. strMessage += "Received " + QString::number(dwRetVal) + " icmp message responses \n";
  64. strMessage += "Information from the first response: ";
  65. }
  66. else {
  67. strMessage += "Received " + QString::number(dwRetVal) + " icmp message response \n";
  68. strMessage += "Information from the first response: ";
  69. }
  70. strMessage += "Received from ";
  71. strMessage += inet_ntoa( ReplyAddr );
  72. strMessage += "\n";
  73. strMessage += "Status = " + pEchoReply->Status;
  74. strMessage += "Roundtrip time = " + QString::number(pEchoReply->RoundTripTime) + " milliseconds \n";
  75. } else {
  76. strMessage += "Call to IcmpSendEcho failed.\n";
  77. strMessage += "IcmpSendEcho returned error: ";
  78. strMessage += QString::number(GetLastError());
  79. }
  80.  
  81. ui->textEdit->setText(strMessage); // Отображаем информацию о полученных данных
  82. free(ReplyBuffer); // Освобождаем память
  83. }

Итог

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

Демонстрация работы приложения приведена в видеоуроке.

Видеоурок

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

АК
  • Ақп. 5, 2024, 12:50 Т.Қ.
  • (өңделген)

Без строки

  1. #include <QRegularExpressionValidator>

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

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз