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