Базы данныхИнтернетКомпьютерыОперационные системыПрограммированиеСетиСвязьРазное
Поиск по сайту:
Подпишись на рассылку:

Назад в раздел

Реализация ping с помощью ICMP API

div.main {margin-left: 20pt; margin-right: 20pt} Реализация ping с помощью ICMP API Владимир Артемов

При работе в сети Интернет в некоторых случаях необходимо знать текущее состояние как локального, так и удаленных хостов (имеет ли локальный хост в данный момент возможность выхода в сеть Интеренет, доступен ли некий удаленный хост и т.д.)

Общеизвестно, что для указанных целей используется утилита ping. В данной статье описана реализация на Delphi возможностей этой программы с помощью ICMP API.

Принцип работы ping-а основан на использовании протокола ICMP - Internet Control Message Protocol (протокол управляющих, или контрольных, сообщений).
С помощью ICMP хосты в сети обмениваются различной служебной информацией (информацией о смене маршрута, уменьшении скорости передачи, недоступности какого-либо адреса и т.д.) В основе протокола ICMP лежит понятие сообщений. Сообщения ICMP протокола, как правило, оповещают об ошибках, возникающих при обработке датаграмм. ICMP использует основные свойства протокола IP, как если бы он являлся протоколом более высокого уровня. На самом же деле ICMP является составной частью IP.

Одним из типов сообщений протокола является т.н. "эхо-запрос". Получив "эхо-запрос" хост обязан ответить пославшему "эхо-ответом". По сути, "эхо-запрос" и "эхо-ответ" отличаются лишь адресами отправителя и получателя и кодом типа сообщения (тип 8 - "эхо-запрос", тип 0 - "эхо-ответ").
Более подробно протокол ICMP описан в RFC792.

Реализации утилиты ping на различных платформах существенно отличаются. Так, в ОС UNIX используются RAW sockets (необработанные, "сырые" сокеты), а в ОС Windows всех версий - т.н. ICMP API. Использование ICMP API оправдано только в ОС Windows NT и 2000, т.к. для использования RAW sockets в этих ОС необходимы права Администратора.

Из документации Microsoft:
"Icmp.dll предоставляет возможность разрабатывать приложения, использующие ICMP (ping, traceroute) без использования Winsock. Если разработчик пишет под платформу, поддерживающую Winsock, предпочтительнее использовать механизм Winsock RAW sockets.
Однако, при написании программ под Windows NT и Windows 2000 следует учитывать, что механизм RAW sockets доступен для использования только членам группы Administrators. Все функции в icmp.dll не рассматриваются как часть Win 32 API и не будут поддерживаться в следующих реализациях".

На практике, во всех 32-разрядных версиях Windows есть и используется библиотека icmp.dll.
Так что, на данный момент, можно безбоязненно ее использовать.

ICMP APIОпределения типов и прототипы функций для Microsoft icmp.dll:
type ip_option_information = record // Информация заголовка IP (Наполнение // этой структуры и формат полей описан в RFC791. Ttl : u_char; // Время жизни (используется traceroute-ом) Tos : u_char; // Тип обслуживания, обычно 0 Flags : u_char; // Флаги заголовка IP, обычно 0 OptionsSize : u_char; // Размер данных в заголовке, обычно 0, максимум 40 OptionsData : PChar; // Указатель на данные end; icmp_echo_reply = record Address : DWORD; // Адрес отвечающего Status : u_long; // IP_STATUS (см. ниже) RTTime : u_long; // Время между эхо-запросом и эхо-ответом // в миллисекундах DataSize : u_short; // Размер возвращенных данных Reserved : u_short; // Зарезервировано Data : Pointer; // Указатель на возвращенные данные Options : ip_option_information; // Информация из заголовка IP end; PIPINFO = ^ip_option_information; PVOID = Pointer; function IcmpCreateFile() : THandle; stdcall; external 'ICMP.DLL' name 'IcmpCreateFile'; function IcmpCloseHandle(IcmpHandle : THandle) : BOOL; stdcall; external 'ICMP.DLL' name 'IcmpCloseHandle'; function IcmpSendEcho( IcmpHandle : THandle; // handle, возвращенный IcmpCreateFile() DestAddress : u_long; // Адрес получателя (в сетевом порядке) RequestData : PVOID; // Указатель на посылаемые данные RequestSize : Word; // Размер посылаемых данных RequestOptns : PIPINFO; // Указатель на посылаемую структуру // ip_option_information (может быть nil) ReplyBuffer : PVOID; // Указатель на буфер, содержащий ответы. ReplySize : DWORD; // Размер буфера ответов Timeout : DWORD // Время ожидания ответа в миллисекундах ) : DWORD; stdcall; external 'ICMP.DLL' name 'IcmpSendEcho';

Из "WinSock.pas" type u_char = Char; u_short = Word; u_long = Longint;

Из "Windows.pas" type DWORD = LongWord; BOOL = LongBool;

Функция IcmpSendEcho() посылает ICMP эхо-запрос по заданному IP адресу и помещает все ответы, полученные за время заданного таймаута, в буфер ответов (ReplyBuffer). Перед использованием функций Icmp.dll необходимо вызвать функцию WSAStartup() из Winsock.
Если этого не сделать, то первый же вызов функции IcmpSendEcho() приведет к ошибке 10091 (WSASYSNOTREADY).

Следует заметить, что ответы, помещенные в буфер, необязательно будут сообщениями "эхо-ответ". Возможно, что в ответ на "эхо-запрос" придут сообщения ICMP о возникших ошибках. Естественно, эти сообщения так же будут помещены в ReplyBuffer.
Размер буфера ответов должен быть достаточным для приема хотя бы одного ответа, будь то "эхо-ответ" или сообщение об ошибке. Отсюда, указанный размер складывается из размера самой сруктуры icmp_echo_reply плюс Max(RequestSize, 8), т.к. сообщение ICMP об ошибке составляет 8 байт.
Функция IcmpSendEcho() возвращает количество ответов (или структур icmp_echo_reply) в массиве ReplyBuffer. Если функция возвратила 0, то для более детальной диагностики возникшей ошибки используется функция GetLastError().

Из "Windows.pas" function GetLastError: DWORD;

Коды ошибок в поле Status структуры icmp_echo_reply, если ответ не является "эхо-ответом" (IP_STATUS): IP_STATUS_BASE 11000 IP_SUCCESS 0 IP_BUF_TOO_SMALL 11001 IP_DEST_NET_UNREACHABLE 11002 IP_DEST_HOST_UNREACHABLE 11003 IP_DEST_PROT_UNREACHABLE 11004 IP_DEST_PORT_UNREACHABLE 11005 IP_NO_RESOURCES 11006 IP_BAD_OPTION 11007 IP_HW_ERROR 11008 IP_PACKET_TOO_BIG 11009 IP_REQ_TIMED_OUT 11010 IP_BAD_REQ 11011 IP_BAD_ROUTE 11012 IP_TTL_EXPIRED_TRANSIT 11013 IP_TTL_EXPIRED_REASSEM 11014 IP_PARAM_PROBLEM 11015 IP_SOURCE_QUENCH 11016 IP_OPTION_TOO_BIG 11017 IP_BAD_DESTINATION 11018 IP_ADDR_DELETED 11019 IP_SPEC_MTU_CHANGE 11020 IP_MTU_CHANGE 11021 IP_UNLOAD 11022 IP_GENERAL_FAILURE 11050 MAX_IP_STATUS IP_GENERAL_FAILURE IP_PENDING 11255

Ниже приведен базовый фрагмент кода, необходимый для однократного посыла эхо-запроса: procedure TForm1.Button1Click(Sender: TObject); var hIP : THandle; pingBuffer : array [0..31] of Char; pIpe : ^icmp_echo_reply; pHostEn : PHostEnt; wVersionRequested : WORD; lwsaData : WSAData; error : DWORD; destAddress : In_Addr; begin // Создаем handle hIP := IcmpCreateFile(); GetMem( pIpe, sizeof(icmp_echo_reply) + sizeof(pingBuffer)); pIpe.Data := @pingBuffer; pIpe.DataSize := sizeof(pingBuffer); wVersionRequested := MakeWord(1,1); error := WSAStartup(wVersionRequested,lwsaData); if (error <> 0) then begin Memo1.SetTextBuf('Error in call to '+ 'WSAStartup().'); Memo1.Lines.Add('Error code: '+IntToStr(error)); Exit; end; pHostEn := gethostbyname('delphi.mastak.com'); error := GetLastError(); if (error <> 0) then begin Memo1.SetTextBuf('Error in call to'+ 'gethostbyname().'); Memo1.Lines.Add('Error code: '+IntToStr(error)); Exit; end; destAddress := PInAddr(pHostEn^.h_addr_list^)^; // Посылаем ping-пакет Memo1.Lines.Add('Pinging ' + pHostEn^.h_name+' ['+ inet_ntoa(destAddress)+'] '+ ' with '+ IntToStr(sizeof(pingBuffer)) + ' bytes of data:'); IcmpSendEcho(hIP, destAddress.S_addr, @pingBuffer, sizeof(pingBuffer), Nil, pIpe, sizeof(icmp_echo_reply) + sizeof(pingBuffer), 5000); error := GetLastError(); if (error <> 0) then begin Memo1.SetTextBuf('Error in call to'+ 'IcmpSendEcho()'); Memo1.Lines.Add('Error code: '+IntToStr(error)); Exit; end; // Смотрим некоторые из вернувшихся данных Memo1.Lines.Add('Reply from '+ IntToStr(LoByte(LoWord(pIpe^.Address)))+'.'+ IntToStr(HiByte(LoWord(pIpe^.Address)))+'.'+ IntToStr(LoByte(HiWord(pIpe^.Address)))+'.'+ IntToStr(HiByte(HiWord(pIpe^.Address)))); Memo1.Lines.Add('Reply time: '+IntToStr(pIpe.RTTime)+' ms'); IcmpCloseHandle(hIP); WSACleanup(); FreeMem(pIpe); end;

Разумеется, это только "скелет". Многие возможные ошибки необрабатываются.

Для работы этого фрагмента надо: создать форму TForm1; поместить в нее компонент TMemo1:TMemo и кнопку TButton1:TButton; в разделе Implementation поместить описания типов и функций Icmp.dll (см. выше); сопоставить событию OnClick кнопки приведенную функцию.


  • Главная
  • Новости
  • Новинки
  • Скрипты
  • Форум
  • Ссылки
  • О сайте




  • Emanual.ru – это сайт, посвящённый всем значимым событиям в IT-индустрии: новейшие разработки, уникальные методы и горячие новости! Тонны информации, полезной как для обычных пользователей, так и для самых продвинутых программистов! Интересные обсуждения на актуальные темы и огромная аудитория, которая может быть интересна широкому кругу рекламодателей. У нас вы узнаете всё о компьютерах, базах данных, операционных системах, сетях, инфраструктурах, связях и программированию на популярных языках!
     Copyright © 2001-2024
    Реклама на сайте