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








