IP-телефония — практические подходы к реализации схемы PC—PC
В настоящее время интерес к IP-телефонии очень высок, что связано с возможностью существенной экономии денег на телефонных переговорах. Начало данной технологии было положено в 1995 году, когда была реализована схема соединения двух компьютеров (PC—-PC), что означает возможность звонить с одного компьютера на другой. Номером телефона является ваш IP-адрес. Затраты на поддержание такого соединения равняются нулю, поскольку не требуется никаких дополнительных аппаратных средств, за исключением имеющихся в компьютере звуковой платы и микрофона.
Рассказывать о развитии IPT можно очень долго, но эта информация уже неоднократно публиковалась, так что не имеет смысла останавливаться на этой теме. Цель этой статьи — показать читателям один из способов реализации схемы PC—PC на программном уровне, используя для этого пакет Microsoft Visual C++ 6.0. Думаю, вы найдете здесь для себя много интересного, поскольку мы будем рассматривать следующие аспекты:
- чтение/запись в реестре;
- передача данных в сети;
- быстрый опрос сети;
- управление звуковыми устройствами.
Предлагаемый вашему вниманию курс рассчитан на тех, кто уже имеет определенный опыт работы в Visual C++ Platform SDK Windows Multimedia и Windows Socket.
Все модули были протестированы на ОС Windows 98 и Windows NT 4.0. Сеть — витая пара 10 Mбит.
Рассмотрим схему и функции, которые были реализованы в программе .
Приложение состоит из двух основных постоянно работающих потоков, которые запускаются при инициализации программы. В первом потоке запускается Клиент, во втором Сервер. Клиент и Сервер расположены на одном компьютере, поэтому нет необходимости организовывать выделенный сервер. После инициализации Сервер входит в режим ожидания и остается в нем до тех пор, пока не получит от удаленного Клиента запрос на соединение. Ваш Клиент активен. У Клиента существует три возможности.
I. Передача сообщения/документа:
- Выбор сервера на соединение (опрос сети);
- Подготовка сообщения/документа;
- Соединение и передача сообщения/документа с сопутствующей информацией об отправителе;
- Завершение сеанса подключения.
II. Соединение для разговора:
- Определение вашего IP-адреса;
- Выбор сервера на соединение (опрос сети);
- Соединение вашей клиентской части с удаленным Сервером и передача информации о вас (ваш IP-адрес);
- Возвратная реакция удаленного сервера на ваш запрос. Удаленный Клиент производит дополнительное соединение с вашим Сервером;
- Инициализация звуковой платы на In и Out (Сервер управляет Out, Клиент — In);
- Считывание и передача данных Клиентом, параллельно производятся прием и воспроизведение звуковых данных Сервером;
- Закрытие устройств;
- Завершение сеанса подключения.
III. Работа с параметрами.
Не буду полностью расписывать, как программно реализована эта схема, рассмотрим только самое главное.
Итак, приступим...
Занятие 1. Чтение/запись в реестр
Как узнать вашу операционную систему, IP-адрес, имя компьютера, имя пользователя?
Большую часть информации о системе можно обнаружить в системном реестре ОС . (Для просмотра реестра вы можете использовать программу, входящую в комплект ОС, regedit.exe.) Считать ее можно, выполнив следующую последовательность команд:
// ---------- Описание переменных
HKEY hKey;
DWORD dwType, dwCount;
CString strValue;
//----------- Определить имя OC
RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion",
0,KEY_READ, &hKey);
RegQueryValueEx (hKey,"ProductName", NULL,
&dwType, NULL, &dwCount);
RegQueryValueEx (hKey,"ProductName", NULL,
&dwType, (LPBYTE)strValue.GetBuffer(dwCount/
sizeof(TCHAR)), &dwCount);
strValue.ReleaseBuffer();
RegCloseKey(hKey);
Функция RegOpenKeyEx позволяет открыть папку реестра, в которой расположена интересующая вас переменная. Параметры задают путь и маску доступа. Обратите внимание, что переход между папками осуществляется "\\", а не "\" — это важно. Если операция выполнена корректно, то ключ-ссылка на папку записана в переменную hKey.
Функция RegQueryValueEx позволит считать значение интересующей вас переменной, в данном случае это ProductName. В первый раз, выполняя функцию RegQueryValueEx, мы определим тип и размер буфера данных. Второй раз — считываем значение в буфер переменной strValue. Следующий шаг — освободить буфер и ключ-ссылку.
После выполнения этого программного кода в переменной strValue должно храниться имя операционной системы, установленной на ваш компьютер. Здесь необходимо оговориться: это справедливо, если вы используете ОС Windows 98. В противном случае значение будет пустым — это связано с тем, что реестры OC Windows 98 и Windows NT не совпадают. Но для нас этого вполне достаточно: теперь мы уже ориентируемся, в какой ОС находимся, и в дальнейшем просто разделить модули работы с реестром Windows 98 и Windows NT. Если вы хотите использовать данный программный код для своих целей, то достаточно будет изменить путь к переменной. Однако при этом следует помнить, что таким образом вы сможете считать только текстовую информацию, но не битовую (ее мы рассмотрим чуть позже).
Имя компьютера и имя пользователя определяются по сходной схеме: необходимо только заменить путь в функции RegOpenKeyEx и имя переменной в функции RegQueryValueEx.
Определение имени компьютера | ||
---|---|---|
98 | RegOpenKeyEx | "SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName" |
RegQueryValueEx | "ComputerName" | |
NT | RegOpenKeyEx | "SYSTEM\\ControlSet001\\Control\\ComputerName\\ActiveComputerName" |
RegQueryValueEx | "Computer Name" | |
Определение имени пользователя | ||
98 | RegOpenKeyEx | "SYSTEM\\CurrentControlSet\\Control" |
RegQueryValueEx | "Current User" | |
NT | RegOpenKeyEx | "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon" |
RegQueryValueEx | "DefaultUserName" |
С определением IP-адреса дело обстоит несколько сложнее, поскольку приходится учитывать не только то, в какой ОС вы находитесь, но и то, по какой схеме работаете (с прямым IP-адресом или используя сервер DHCP), а также сколько у вас установлено сетевых протоколов (TCP/IP, IPX/SPX, NetBEUI...).
Если у вас — ОС Windows 98 и установлен прямой IP-адрес, то информацию о нем можно получить в одной из папок реестра (0000, 0001, 0002...). В какой именно — зависит от привязок.
Нам будет необходимо просмотреть их последовательно, один за другим, пока мы не отыщем там интересующую нас переменную. Ниже представлен один из методов определения вашего прямого IP-адреса.
CString Temp="SYSTEM\\CurrentControlSet\\Services\\
Class\\NetTrans\\000";
CString IPAddress;
for (i=0;i<10;i++)
{
bufTemp.Format("%d",i);
RegOpenKeyEx(HKEY_LOCAL_MACHINE,
mTemp+bufTemp,0,KEY_READ,&hKey);
RegQueryValueEx(hKey,"IPAddress", NULL, &dwType,
NULL, &dwCount);
RegQueryValueEx(hKey,"IPAddress", NULL, &dwType,
(LPBYTE)strValue.GetBuffer(dwCount/
sizeof(TCHAR)), &dwCount);
strValue.ReleaseBuffer();
RegCloseKey(hKey);
if ((strValue!="0.0.0.0")&&(strValue!=""))
{
IPAddress=strValue;
break;
}
}
Переменной Temp присваивается строка, в которой содержится часть пути к переменной реестра IPAddress. Далее организован цикл поиска по десяти папкам — от '0000' до '0009' (этого вполне достаточно). В первых трех строчках цикла производится корректировка пути к переменной реестра. За ними следует уже знакомая вам последовательность команд чтения. Если поиск в папке прошел успешно, то мы производим выход из цикла, в противном случае — переходим к следующей папке. По окончании поиска мы будем иметь в переменной strValue значение нашего IP-адреса.
Поиск IP-адреса, выделенного сервером DHCP, производится практически по той же схеме — только IP-адрес представлен в реестре уже не текстовой информацией, а числовой. Поэтому добавим строку для конвертирования в текстовой формат:
bufTemp="";
CString strDhcp="";
CString mTemp="System\\CurrentControlSet\\Services\\VxD\\
DHCP\\DhcpInfo0";
for (i=0;i<10;i++)
{
bufTemp.Format("%d",i);
RegOpenKeyEx(HKEY_LOCAL_MACHINE,
mTemp+bufTemp, 0,KEY_READ,&hKey);
RegQueryValueEx(hKey,"DhcpIPAddress", NULL, &dwType,
NULL, &dwCount);
RegQueryValueEx(hKey,"DhcpIPAddress", NULL, &dwType,
(LPBYTE)strDhcp.GetBuffer(dwCount/sizeof(TCHAR)),
&dwCount);
strDhcp.ReleaseBuffer();
RegCloseKey(hKey);
if (strDhcp!="")
{
strValue.Format("%d%s%d%s%d%s%d",strDhcp[0],".",
strDhcp[1],".",strDhcp[2],".",strDhcp[3]);
IPAddress=strValue;
break;
}
}
Если вы используете Windows NT 4.0 и прямой адрес IP, то вам понадобится предварительно определить имя сетевой платы. В том случае, если у вас не одна сетевая карта, нужно определить, какую вы будете использовать. Для этого откорректируйте концовку пути RegOpenKeyEx:
CString NameNetCard;
RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\
Microsoft\\Windows NT\\CurrentVersion\\
NetworkCards\\1", 0,KEY_READ,&hKey);
RegQueryValueEx(hKey,"ServiceName", NULL, &dwType,
NULL, &dwCount);
RegQueryValueEx(hKey,"ServiceName", NULL, &dwType, (LPBYTE)NameNetCard.GetBuffer(dwCount/sizeof(TCHAR)),
&dwCount);
NameNetCard.ReleaseBuffer();
RegCloseKey(hKey);
В переменой NameNetCard хранится имя вашей сетевой карты. Используйте его для установки пути к IP-адресу вашей сетевой карты:
CString TempVar="SYSTEM\\ControlSet001\\Services\\"+
NameNetCard+"\\Parameters\\Tcpip";
RegOpenKeyEx(HKEY_LOCAL_MACHINE,TempVar,0,
KEY_READ, hKey);
RegQueryValueEx(hKey, "IPAddress", NULL, &dwType,
NULL, dwCount);
pBytes = dwCount;
ppData = new BYTE[dwCount];
RegQueryValueEx(hKey, "IPAddress", NULL, &dwType, ppData, &dwCount);
RegCloseKey(hKey);
IPAddress=ppData;
delete [] ppData;
ppData = NULL;
Обратите внимание — процедура несколько видоизменена. Это связано с тем, что нам приходится читать информацию, представленную в битовом формате. После получения 16-ричных кодов неявно преобразуем их к текстовому формату.
Чтобы определить IP-адрес, назначенный вам DHCP-сервером, выполните следующую процедуру:
CString TempVar="SYSTEM\\ControlSet001\\Services\\"+
NameNetCard+"\\Parameters\\Tcpip";
RegOpenKeyEx(HKEY_LOCAL_MACHINE,TempVar,0
KEY_READ,&hKey);
RegQueryValueEx(hKey,"DhcpIPAddress", NULL, &dwType,
NULL, &dwCount);
RegQueryValueEx(hKey,"DhcpIPAddress", NULL, &dwType,
(LPBYTE)strValue.GetBuffer(dwCount/sizeof(TCHAR)),
&dwCount);
strValue.ReleaseBuffer();
RegCloseKey(hKey);
Итак, вы имеете некоторое преставление о том, как можно получить информацию из реестра. Расскажем еще об одной возможности, которая вам наверняка пригодится. При создании вашей программы в реестре организуется папка для переменных под вашу программу. Здесь вы можете сохранять информацию, имеющую отношение к вашей программе: размер окон, местоположение, установки и многое другое. Организуется эта папка достаточно просто — в модуле инициализации программы нужно изменить строку (см. Руководство):
SetRegistryKey(_T("Имя Вашего проекта"));
В реестре появится папка Имя Вашего проекта, место положения которой:
HKEY_CURRENT_USER\Software\Имя проекта\\
Работа с папкой наглядно продемонстрирована ниже:
if (GetProfileInt("Sound", "Reg_mMHz",-1)==-1)
WriteProfileInt("Sound", "Reg_mMHz", 22050);
if (GetProfileString("NameFile", "f_in","")=="")
WriteProfileString("NameFile", "f_in", res\\Ringin.wav");
Функцией GetProfileInt мы производим чтение из подкаталога Sound каталога Имя Вашего проекта целочисленной переменной Reg_mMHz. Если переменная не была найдена, то будет возвращено значение, указанное в параметре функции (-1), и функция WriteProfileInt создаст необходимый нам подкаталог и переменную и присвоит ей значение по умолчанию (22050). Аналогично можно производить чтение/запись строковых переменных.
Теперь приступайте к практике. Только будьте осторожны — не повредите имеющуюся в реестре информацию. Последствия могут быть непредсказуемы, вплоть до переустановки операционной системы.
КомпьютерПресс 2'2001