Теория

 

Команда ping служит для принудительного вызова ответа конкретной машины. Для этого используется дейтаграмма ECHO_REQUEST протокола ICMP. Это протокол низкого уровня, который не требует наличия серверных процессов на зондируемой машине; это хороший способ убедится в том, что питание машины включено и IP находится в поднятом состоянии. Успешный результат использования команды ping вовсе не обязательно означает, что выполняются какие-то сервисные программы высокого уровня.

 

Ping - хорошее средство проверки правильности конфигурации сети, поскольку в выполнении этой команды участвуют система маршрутизации, схемы разрешения адресов и сетевые шлюзы. Если данная команда не работает - можете быть совершенно уверены, что более сложные средства тем более не функционируют. Несмотря на свою простоту, ping - одна из главных рабочих лошадок, использующихся при отладке сетей.

 

Практика

 

Для практической реализации, как всегда, можно использовать несколько подходов. Первый из них - использование низкоуровневых функций API (к примеру, встроенных в библиотеку ICMP.DLL). Второй - использование высокоуровневых компонентов (к примеру, Indy IdICMPClient).

 

И у первого, и у второго способа есть свои позитивные и негативные моменты. Так, при использовании функций API откомпилированный код будет иметь гораздо меньшие размеры, нежели при использовании высокоуровневых компонентов,- да и производительность его будет выше (например, при одновременном пинге одной подсети с использованием потоков).

 

С другой стороны, компоненты можно использовать, имея только отдаленное представление о работе с протоколом ICMP, а также об использовании Windows API. Но, в то же время, компоненты порождают неоправданно большой исполняемый код, да и производительность в этом случае ниже. К счастью, сегодня разработчиками процессоров и памяти почти стерта грань между производительностью кода, написанного с использованием Windows API, и кода, написанного с использованием средств более высокого уровня абстракции (в нашем случае - компонентов Delphi).

 

На всякий случай приведем примеры пинга, написанного как с использованием WinAPI так и с использованием компонентов Indy.

 

Windows API

 

При использовании Windows API для написания функции пинга воспользуемся библиотекой ICMP (icmp.dll), которая предоставляет интерфейс для работы с одноименным протоколом. В этой библиотеке реализованы три функции, с которыми в дальнейшем мы будем работать. В интерпретации Delphi их объявления выглядят следующим образом:

 

function IcmpCreateFile:Thandle; StdCall;

function IcmpCloseHandle (H:Thandle):Bool; StdCall;

function IcmpSendEcho (IcmpHandle:Thandle;
DestinationAddress:TipAddr;
RequestData:pointer;
RequestSize:word;
RequestOptions:POption_Information;
ReplyBuffer:pointer;
ReplySize:integer;
Timeout:integer):Integer; stdcall;

 

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

 

Остановимся подробнее на функции IcmpSendEcho. Принимаемые ею параметры "звучат" следующим образом:

  • IcmpHandle - идентификатор соединения, установленного при помощи IcmpCreateFile;
  • DestinationAddress - адрес пингуемого хоста;
  • RequestData - буфер с данными, которые посылаются при запросе;
  • RequestSize - размер буфера запроса;
  • RequestOptions - дополнительные свойства запроса;
  • ReplyBuffer - адрес буфера для приема результата;
  • ReplySize - размер буфера приема;
  • Timeout - время, в течение которого мы ожидаем ответа от хоста;
  • результат функции - количество записей типа ICMP_ECHO_REPLY, сохраненных в ReplyBuffer. Статус каждой записи хранится в соответствующем поле этой записи. При неудачном вызове функция возвращает значение NULL; дополнительная информация доступна при вызове GetLastError.

Структура ICMP_ECHO_REPLY имеет следующий вид:

 

Ticmp_echo_reply=record
Address: TipAddr; // Ответивший адрес
Status: integer; // Статус ответа
RoundTripTime: integer; // Время прохождения пакета
DataSize: word; // Размер данных ответа в байтах
Reserved: word; // Зарезервировани
Data: pointer; // Указатель на буфер с ответом
Options: Toption_Information; // Опции ответа.
End;

 

Помимо нее, мы можем использовать расширенный вариант структуры ICMP_ECHO_REPLY:

 

TsmICMP_Echo_Reply=record
Address: TipAddr; // Ответивший адрес
Status: integer; // Статус ответа
RoundTripTime: integer; // Время прохождения пакета
DataSize: word; // Размер данных ответа в байтах
Reserved: word; // Зарезервировани
Data: pointer; // Указатель на буфер с ответом
Options: Toption_Information; // Опции ответа.
Data: array [0..255] of Char;
end;
 

Теперь для реализации пинга хоста мы:

  • создаем соединение;
  • вызываем ICMPSendEcho;
  • обрабатываем результат;
  • закрываем соединение.

Эти действия удобно оформить в виде процедуры:

 

procedure Ping (const Address, EchoString: PChar;
var PingReply: TsmICMP_Echo_Reply;
const PingTimeout: Integer = 500);
var
IPAddress: TipAddr;
ICMPPort: THandle;
begin
// Конвертация IP в понятный для API формат
IPAddress:= inet_addr (Address);
// Проверка корректности конвертации
if (IPAddress = INADDR_NONE) then
begin
raise Exception.Create ('Function call inet_addr failed. ' +
'The IP address is probably invalid.');
end;
// Открытие соединения
ICMPPort:= IcmpCreateFile ();
// Проверка правильности открытия
if (ICMPPort = INVALID_HANDLE_VALUE) then
begin
raise Exception.Create ('Function call IcmpCreateFile failed.');
end;
// Отправка запроса "пинг"
IcmpSendEcho (ICMPPort, IPAddress,
EchoString, Length (EchoString), nil,
@PingReply, SizeOf (PingReply), PingTimeout);
// Закрытие соединения
IcmpCloseHandle (ICMPPort);
end;
 

Теперь при использовании в коде программы конструкции:

 

Ping ('127.0.0.1',nil,Reply,5000);
 

в переменной Reply мы получим результат пинга.

 

Использование Indy

 

Те, кто не знакомы с практикой программирования с использованием Windows API, могут воспользоваться встроенными в Delphi компонентами для работы с сетью. В частности, для реализации пинга на уровне объектно-ориентированного программирования воспользуемся компонентом IdICMPClient из состава Indy.

 

Для этого создадим сначала на пустой форме экземпляр класса TIdICMPClient, перетащив его с палитры компонентов Indy Clients. Затем поместим на форму стандартную кнопку (TButton) и в ее реакции на нажатие мышкой запишем код:

 

Self.IdIcmpClient1.Host:='localhost';//вместо 127.0.0.1 здесь можно использовать имя "localhost"
Self.IdIcmpClient1.Ping;

 

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

 

TidIcmpClient.OnReply (Sender:TComponent; const AReplyStatus:TReplyStatus);
 

- в котором мы реализуем вывод данных пинга на экран:

 

procedure TForm1.IdIcmpClient1Reply (ASender: TComponent;
const AReplyStatus: TReplyStatus);
begin
ListBox1.Items.Add ('Reply:'+IntToStr (AReplyStatus.MsRoundTripTime));
end;
 

Послесловие

 

Конечно, для пинга удаленной машины вы всегда можете обратиться к соответствующей программе командной строки, поскольку 100% операционных систем поддерживает эту команду. Но не всегда дело ограничивается одним пингом - особенно если вы реализуете собственный интерфейс поверх IP. В таком случае вызов внешней программы при каждой проверке - слишком большая роскошь.

 

Кроме того, поняв, как работать с ICMP, вы сможете использовать эти возможности и в других - в том числе и "военных" - целях.

 

Листинг 1. Определения функций ICMP.DLL

 

unit icmp;
interface
uses windows;
Const
// IP_STATUS codes returned from IP APIs

IP_STATUS_BASE = 11000;
IP_SUCCESS = 0;
IP_BUF_TOO_SMALL = (IP_STATUS_BASE + 1);
IP_DEST_NET_UNREACHABLE = (IP_STATUS_BASE + 2);
IP_DEST_HOST_UNREACHABLE = (IP_STATUS_BASE + 3);
IP_DEST_PROT_UNREACHABLE = (IP_STATUS_BASE + 4);
IP_DEST_PORT_UNREACHABLE = (IP_STATUS_BASE + 5);
IP_NO_RESOURCES = (IP_STATUS_BASE + 6);
IP_BAD_OPTION = (IP_STATUS_BASE + 7);
IP_HW_ERROR = (IP_STATUS_BASE + 8);
IP_PACKET_TOO_BIG = (IP_STATUS_BASE + 9);
IP_REQ_TIMED_OUT = (IP_STATUS_BASE + 10);
IP_BAD_REQ = (IP_STATUS_BASE + 11);
IP_BAD_ROUTE = (IP_STATUS_BASE + 12);
IP_TTL_EXPIRED_TRANSIT = (IP_STATUS_BASE + 13);
IP_TTL_EXPIRED_REASSEM = (IP_STATUS_BASE + 14);
IP_PARAM_PROBLEM = (IP_STATUS_BASE + 15);
IP_SOURCE_QUENCH = (IP_STATUS_BASE + 16);
IP_OPTION_TOO_BIG = (IP_STATUS_BASE + 17);
IP_BAD_DESTINATION = (IP_STATUS_BASE + 18);

// The next group are status codes passed up on status indications to
// transport layer protocols.
IP_ADDR_DELETED = (IP_STATUS_BASE + 19);
IP_SPEC_MTU_CHANGE = (IP_STATUS_BASE + 20);
IP_MTU_CHANGE = (IP_STATUS_BASE + 21);
IP_UNLOAD = (IP_STATUS_BASE + 22);
IP_GENERAL_FAILURE = (IP_STATUS_BASE + 50);
MAX_IP_STATUS = IP_GENERAL_FAILURE;
IP_PENDING = (IP_STATUS_BASE + 255);

// Values used in the IP header Flags field.
IP_FLAG_DF = $2; // Don't fragment this packet.

// Supported IP Option Types.
// These types define the options which may be used in the OptionsData field
// of the ip_option_information structure. See RFC 791 for a complete
// description of each.
IP_OPT_EOL = 0; // End of list option
IP_OPT_NOP = 1; // No operation
IP_OPT_SECURITY = $82; // Security option
IP_OPT_LSRR = $83; // Loose source route
IP_OPT_SSRR = $89; // Strict source route
IP_OPT_RR = $7; // Record route
IP_OPT_TS = $44; // Timestamp
IP_OPT_SID = $88; // Stream ID (obsolete)

MAX_OPT_SIZE = 40; // Maximum length of IP options in bytes


Type

TIPAddr=integer; // An IP address.
TIPMask=integer; // An IP subnet mask.
TIP_STATUS=Integer; // Status code returned from IP APIs.

POption_Information=^TOption_Information;
TOption_Information=record
Ttl:byte; // Time To Live
Tos:byte; // Type Of Service
Flags:byte; // IP header flags
OptionsSize:byte; // Size in bytes of options data
OptionsData:pointer; // Pointer to options data
end;
Picmp_echo_reply=^Ticmp_echo_reply;
Ticmp_echo_reply=record
Address:TipAddr; // Replying address
Status:integer; // Reply IP_STATUS
RoundTripTime:integer; // RTT in milliseconds
DataSize:word; // Reply data size in bytes
Reserved:word; // Reserved for system use
Data:pointer; // Pointer to the reply data
Options:Toption_Information; // Reply options
end;
TsmICMP_Echo_Reply=record
Address:TipAddr; // Replying address
Status:integer; // Reply IP_STATUS
RoundTripTime:integer; // RTT in milliseconds
DataSize:word; // Reply data size in bytes
Reserved:word; // Reserved for system use
DataPtr:pointer; // Pointer to the reply data
Options:Toption_Information; // Reply options
Data: array [0..255] of Char;
end;

function IcmpCreateFile:Thandle; StdCall;
function IcmpCloseHandle (H:Thandle):Bool; StdCall;
function IcmpSendEcho (IcmpHandle:Thandle;DestinationAddress:TipAddr;
RequestData:pointer;RequestSize:word;
RequestOptions:POption_Information;ReplyBuffer:pointer;
ReplySize:integer;Timeout:integer):Integer; stdcall;
Implementation
function IcmpCreateFile; external 'Icmp.Dll';
function IcmpCloseHandle; external 'Icmp.Dll';
Function IcmpSendEcho; external 'Icmp.Dll';
end.
 


Листинг 2. Процедура "пинга"

 

unit pingModule;
interface
uses icmp, Windows;
const
INADDR_NONE: integer = -1;
procedure Ping (const Address, EchoString: PChar;var PingReply: TsmICMP_Echo_Reply;
const PingTimeout: Integer = 500);
function PingStatusToStr (StatusCode: integer): string;
function inet_addr (IPAddress: PChar): TipAddr; StdCall;
implementation
uses Dialogs, SysUtils;
procedure Ping (const Address, EchoString: PChar;
var PingReply: TsmICMP_Echo_Reply;
const PingTimeout: Integer = 500);
var
IPAddress: TipAddr;
ICMPPort: THandle;
begin
IPAddress:= inet_addr (Address);
if (IPAddress = INADDR_NONE) then
begin
raise Exception.Create ('Function call inet_addr failed. ' +
'The IP address is probably invalid.');
end;
ICMPPort:= IcmpCreateFile ();
if (ICMPPort = INVALID_HANDLE_VALUE) then
begin
raise Exception.Create ('Function call IcmpCreateFile failed.');
end;
IcmpSendEcho (ICMPPort, IPAddress,
EchoString, Length (EchoString), nil,
@PingReply, SizeOf (PingReply), PingTimeout);
IcmpCloseHandle (ICMPPort);
end;

function PingStatusToStr (StatusCode: integer): string;
begin
case (StatusCode) of
IP_SUCCESS: Result:= 'IP_SUCCESS';
IP_BUF_TOO_SMALL: Result:= 'IP_BUF_TOO_SMALL';
IP_DEST_NET_UNREACHABLE: Result:= 'IP_DEST_NET_UNREACHABLE';
IP_DEST_HOST_UNREACHABLE: Result:= 'IP_DEST_HOST_UNREACHABLE';
IP_DEST_PROT_UNREACHABLE: Result:= 'IP_DEST_PROT_UNREACHABLE';
IP_DEST_PORT_UNREACHABLE: Result:= 'IP_DEST_PORT_UNREACHABLE';
IP_NO_RESOURCES: Result:= 'IP_NO_RESOURCES';
IP_BAD_OPTION: Result:= 'IP_BAD_OPTION';
IP_HW_ERROR: Result:= 'IP_HW_ERROR';
IP_PACKET_TOO_BIG: Result:= 'IP_PACKET_TOO_BIG';
IP_REQ_TIMED_OUT: Result:= 'IP_REQ_TIMED_OUT';
IP_BAD_REQ: Result:= 'IP_BAD_REQ';
IP_BAD_ROUTE: Result:= 'IP_BAD_ROUTE';
IP_TTL_EXPIRED_TRANSIT: Result:= 'IP_TTL_EXPIRED_TRANSIT';
IP_TTL_EXPIRED_REASSEM: Result:= 'IP_TTL_EXPIRED_REASSEM';
IP_PARAM_PROBLEM: Result:= 'IP_PARAM_PROBLEM';
IP_SOURCE_QUENCH: Result:= 'IP_SOURCE_QUENCH';
IP_OPTION_TOO_BIG: Result:= 'IP_OPTION_TOO_BIG';
IP_BAD_DESTINATION: Result:= 'IP_BAD_DESTINATION';
IP_ADDR_DELETED: Result:= 'IP_ADDR_DELETED';
IP_SPEC_MTU_CHANGE: Result:= 'IP_SPEC_MTU_CHANGE';
IP_MTU_CHANGE: Result:= 'IP_MTU_CHANGE';
IP_UNLOAD: Result:= 'IP_UNLOAD';
IP_GENERAL_FAILURE: Result:= 'IP_GENERAL_FAILURE';
else
Result:= '';
end;
end;


function inet_addr; external 'WSock32.Dll';

end.
 


Листинг 3. Демонстрация пингов

 

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdBaseComponent, IdComponent, IdRawBase, IdRawClient,
IdIcmpClient, StdCtrls, IdUDPBase, IdUDPClient, IdSNTP;

type
TForm1 = class (TForm)
Button1: TButton;
ListBox1: TListBox;
IdIcmpClient1: TIdIcmpClient;
IdSNTP1: TIdSNTP;
procedure Button1Click (Sender: TObject);
procedure IdIcmpClient1Reply (ASender: TComponent;
const AReplyStatus: TReplyStatus);
private
{Private declarations}
public
{Public declarations}
end;

var
Form1: TForm1;


implementation
uses pingModule,icmp;
{$R *.dfm}

procedure TForm1.Button1Click (Sender: TObject);
var Reply:TsmICMP_Echo_Reply;
begin
Self.IdIcmpClient1.Host:='localhost';
Self.IdIcmpClient1.TTL:=192;
Self.IdIcmpClient1.Ping;
Ping ('127.0.0.1',nil,Reply,5000);
ListBox1.Items.Add ('RawReply:'+IntToStr (Reply.RoundTripTime));
end;

procedure TForm1.IdIcmpClient1Reply (ASender: TComponent;
const AReplyStatus: TReplyStatus);
begin
ListBox1.Items.Add ('Reply:'+IntToStr (AReplyStatus.MsRoundTripTime));
end;

end.

2004.05.05
19.03.2009
В IV квартале 2008 г. украинский рынок серверов по сравнению с аналогичным периодом прошлого года сократился в денежном выражении на 34% – до $30 млн (в ценах для конечных пользователей), а за весь календарный год – более чем на 5%, до 132 млн долл.


12.03.2009
4 марта в Киеве компания Telco провела конференцию "Инновационные телекоммуникации", посвященную новым эффективным телекоммуникационным технологиям для решения задач современного бизнеса.


05.03.2009
25 февраля в Киеве компания IBM, при информационной поддержке "1С" и Canonical, провела конференцию "Как сохранить деньги в условиях кризиса?"


26.02.2009
18-19 февраля в Киеве прошел юбилейный съезд ИТ-директоров Украины. Участниками данного мероприятия стали ИТ-директора, ИТ-менеджеры, поставщики ИТ-решений из Киева, Николаева, Днепропетровска, Чернигова и других городов Украины...


19.02.2009
10 февраля в Киеве состоялась пресс-конференция, посвященная итогам деятельности компании "DiaWest – Комп’ютерний світ" в 2008 году.


12.02.2009
С 5 февраля 2009 г. в Киеве начали работу учебные курсы по использованию услуг "электронного предприятия/ учреждения" на базе сети информационно-маркетинговых центров (ИМЦ).


04.02.2009
29 января 2009 года в редакции еженедельника "Computer World/Украина" состоялось награждение победителей акции "Оформи подписку – получи приз!".


29.01.2009
22 января в Киеве компания "МУК" и представительство компании Cisco в Украине провели семинар для партнеров "Обзор продуктов и решений Cisco Small Business"

 

 
 
Copyright © 1997-2008 ИД "Комиздат".