oldi

IP-телефония — практические подходы к реализации схемы PC—PC

(Продолжение. Начало в № 2-3’2001)

С.В. Захаров

Занятие 3. Быстрый опрос сети

Как узнать, кто в сети?

Возможно, вы когда-то использовали или сейчас используете программное обеспечение, с помощью которого происходит передача звуковых данных (разговор). Если это так, вспомните, есть ли там автоматический поиск клиентов или вам предлагается ввести адрес абонента вручную. Я пока не встречал ни одной, где была бы возможность автоматического поиска и выбора абонента. А поскольку мне хотелось облегчить себе жизнь и не создавать базу данных IP-адресов, которые могут и меняться, я создал модуль просмотра сети по TCP\IP-адресу. Если вы работаете в Windows NT и у вас организован Domain, то проблема может быть решена достаточно просто. Visual C++ 6.0 обладает набором функций, позволяющих получить информацию об активных пользователях в сети, хотя не всегда корректную. Это связано с тем обстоятельством, что при выходе пользователя из сети информация в Domain обновляется не сразу. Решение «в лоб» (клиент по очереди перебирает все адреса, пытаясь произвести подключение) здесь не годится — на попытку соединения с отсутствующим в сети абонентом уходит порядка одной минуты, в течение которой происходит поиск абонента. Поэтому просмотр только по правому полю IP-адреса займет около 4 часов.

Ниже приведен пример программного кода, выполнение которого позволяет организовать быстрый просмотр сети в ОС Windows 98/NT:

  // CString MyIPAddress.="117.117.119.53";
CString ShortIP.="117.117.119.";
CString mTemp, bufTemp;
   CStringArray arrayList;
arrayList.SetSize(10,5);
int mSlider=0;
int i=0;
   typedef struct _ihdr {
BYTE  i_type;
BYTE i_code;
USHORT i_cksum;
USHORT i_id;
USHORT i_seq;
ULONG timestamp;
   } IcmpHeader;
 WSADATA wsaData;
 SOCKET    sockRaw;
 SOCKADDR_IN   ClientSocketAddress , from;
 int      frolen=sizeof(from);
 int      timeout = 20;
 int      datasize, bread;
 char     *datapart;
 LPSTR     recvbuf, icmp_data;
  datasize  = 32+sizeof(IcmpHeader);
  recvbuf   = (LPSTR)malloc(1024);
    icmp_data = (LPSTR)malloc(1024);
  WSAStartup(MAKEWORD(2,1),&wsaData);
  sockRaw = socket(AF_INET,SOCK_RAW, IPPROTO_ICMP);
  setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,
(char*)&timeout, sizeof(timeout));
  setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,
(char*)&timeout, sizeof(timeout));
  memset(icmp_data,0,1024);

  ClientSocketAddress.sin_family=AF_INET;
  ClientSocketAddress.sin_port=100;

   ((IcmpHeader*)icmp_data)->i_type = 8;
   ((IcmpHeader*)icmp_data)->i_code = 0;
   ((IcmpHeader*)icmp_data)->i_id=(USHORT)GetCurrentProcessId();
   ((IcmpHeader*)icmp_data)->i_cksum =0;
   ((IcmpHeader*)icmp_data)->i_seq = 0;
   datapart = icmp_data + sizeof(IcmpHeader);
   memset(datapart,'E', datasize - sizeof(IcmpHeader));
for (i=1; i<256; i++)
{  ((IcmpHeader*)icmp_data)->timestamp = GetTickCount();
        ((IcmpHeader*)icmp_data)->i_cksum = 0;
        ((IcmpHeader*)icmp_data)->i_seq = i;
        ((IcmpHeader*)icmp_data)->i_cksum =checksum(
      (USHORT*)icmp_data, datasize);
sprintf(bufTemp,"%d", i);
mTemp= ShortIP+bufTemp;
ClientSocketAddress.sin_addr.S_un.S_addr=inet_addr(mTemp);
bread = sendto(sockRaw,icmp_data,datasize,0,
     (LPSOCKADDR)&ClientSocketAddress,
      sizeof(ClientSocketAddress));
bread = recvfrom(sockRaw,recvbuf,1024,0,
     (LPSOCKADDR)&from,&frolen);
if (bread != SOCKET_ERROR)
{  arrayList.SetAtGrow(mSlider,mTemp);
 mSlider++;
}
}
arrayList.FreeExtra();
free(recvbuf);
free(icmp_data);
closesocket(sockRaw);

Для проверки доступности IP-адреса в сети воспользуемся возможностями сетевого протокола Internet Control Message Protocol (ICMP). Рассмотрим ее. Сначала, как всегда, описываем и инициализируем необходимые нам переменные и структуры. К моменту выполнения данной функции уже необходимо знать свой IP-адрес. Как его узнать, было рассказано на Занятии № 1. Он определяет маску поиска в сети по первым трем полям IP-адреса. Запрос функции WSAStartup инициализирует Winsock.dll и структуру WSADATA. Далее создаем гнездо. Для этого используем функцию socket и в качестве ее параметра передаем значение IPPROTO_ICMP, тем самым указав, что гнездо будет использовать протокол ICMP. Произведем изменения свойств гнезда, ограничив переменной timeout=20 интервал времени (мс), используемого им на прием и передачу данных. В этом нам поможет функция setsockopt. Теперь инициализируем ICMP-заголовок и подготовим датаграмму. Входим в цикл поиска по IP-адресу. Поскольку первые первые три поля у нас определены, изменяем только последнее, четвертое поле адреса (1...255). Интервал поиска описывается в структуре for(). Не устанавливая соединения, передаем датаграмму по указному адресу (sendto) и ждем от него отклика. Если функция recvfrom не возвращает SOCKET_ERROR, то указанный адрес активен. Использование информации recvbuf поможет определить качество нашей локальной сети путем оценки потери информации при передачи и приеме. Активный адрес добавляется в массив arrayList. В итоге получаем в свое распоряжение список активных IP-адресов.

 

Продолжение следует

КомпьютерПресс 4'2001