This article first appeared online at ClarionOnline ( http://www.clariononline.com ) in volume 2 issue 10 (May 1999)


Определение имени и адреса TCP/IP

В этой статье я собираюсь дать вам базовую информацию, необходимую для определения имени компьютера и/или его IP адреса.

Зачем вам это нужно? Есть несколько причин. Вы можете использовать их как часть алгоритма защиты от копирования, или только для диагностических целей в сетевом окружении.

Если ваше приложение использует программно-закодированные имена файлов, которые динамически присваиваются во время выполнения, для передачи данных серверу, то вы можете по IP адресу сервера определить его имя и затем использовать имя компьютера в программно-закодированных именах файлов.

И это работает даже в более крупном масштабе. Например, если мы знаем адремс веб-сайта (наподобие http://www.clariononline.com ), то мы можем определить его IP адрес просто пингованием сервера. Когда мы знаем его IP адрес, мы можем определить имя компьютера. Если эта машина имеет разделяемые каталоги, для которых мы имеем права записи, то мы можем использовать это имя в приложении и открыть файлы данных прямо на этой машине, или только просто копировать файлы на нее.

Мы сделаем все это используя библиотеку Winsock. Winsock поставляется в двух видах, 16-и и 32-разрядном, как и все другие библиотеки Windows API. Наш пример программы является 32-разрядным приложением CW, но нет никаких причин, почему мы не можем изменить ее на 16-разрядов, если нужно.

Что такое Winsock ?

Winsock это имя спецификации, которая определяет сетевой программный инерфейс для Microsoft Windows, который основан на стандарте "сокетов", представленный Berkeley Software Distribution (BSD) из University of California at Berkeley.

Мы будем использовать только малую часть функций Winsock API для получения имени компьютера на котором мы находимся, для получения IP адреса компьютера по его имени, и для получения имени любого компьютера с известным IP адресом.

Однако, как обычно, есть пара вещей, которые нам нужно знать.

Во-первых, Winsock API часть использует 4-байтные IP адреса. Мы должны конвертировать обычный "точечно-десятичный" IP адрес, который мы все знаем и любим в 4-байтный long , перед вызовом функции API, и преобразовать назад после возврата из функции. Будет это STRING(4) или LONG совершенно не важно, так как long состоит из четырех байт.

Во-вторых, волнует применение в функциях Winsock множества указателей. Clarion вообще не поддерживает указателей - поэтому вы можете создать достаточно легко используя функцию ADDRESS(), но применение указателей не так легко, как могло бы быть. Чтобы сделать это мы должны использовать рантайм-функцию C MEMCPY(), которая может немного смущать, как я покажу ниже.

В-третьих, вы должны импортировать библиотеку ля линковки. Я прилагаю в этом примере 32-разрядную версию, но вы можете всегда создать свою при помощи LibMaker (поставляемой в качестве примера с дистрибутивом CW) на основе Winsock DLL, который находится в вашем каталоге \Windows или \WINNT.

В любом случае, мы будем использовать следующие функции Winsock API:

! Initialise Winsock
WSAStartup(USHORT,LONG),LONG,PASCAL,NAME('WSASTARTUP'),DLL(dll_mode)
! Get the last error from the runtime
WSAGetLastError(),LONG,RAW,PASCAL,NAME('WSAGETLASTERROR'),DLL(dll_mode)
! Get the name of this machine
GetHostName(*CSTRING,LONG),LONG,RAW,PASCAL,NAME('GetHostName'),DLL(dll_mode)
! Look up a IP address from a name
GetHostByName(LONG),LONG,PASCAL,RAW,NAME('GetHostByName'),DLL(dll_mode)
! Look up a name by address
GetHostByAddr(Long,Long,Long),Long,Pascal,Raw,Name('GetHostByAddr'),Dll(dll_mode)
! Change the 4-byte address into dotted decimal
Inet_NToA(LONG),LONG,PASCAL,NAME('INet_NToA'),DLL(dll_mode)
! Change the dotted-decimal into a 4-byte address
INet_Addr(LONG),ULONG,PASCAL,NAME('INET_ADDR'),DLL(dll_mode)

Прежде чем мы сможем использовать функции Winsock, нужно сначала вызвать функцию WSAStartup. Она инициализирует Winsock DLL, и возвращает подробности о реализации реализации Winsock (которые я всегда игнорирую). Если вы это успешно сделали, можно идти вперед и использовать прочие функции.

Получение имени компьютера

Получить имя компьютера абсурдно просто, как показывает этот фрагмент кода. Когда Winsock работает, просто вызовите GetHostName. Передайте ей CSTRING и длину строки, и она поместит имя компьютера в эту строку.

MachineName Cstring(100)
Code
If GetHostName(Loc:MachineName,100) = 0   
  Message('Local machine name is ' &Clip(Loc:MachineName))
End 

Код возврата = 0 означает, что работа сделана; -1 означает ошибку. В случае ошибки, причину ошибки можно узнать при помощи функции WSAGETLASTERROR().

Получение IP адреса по извесному имени компьютера

Эта задача немного сложнее. Помните проблему CW с указателями? Здесь она действительно проявляется. Для получения IP адреса, вызовите функцию GETHOSTBYNAME().

Передайте указатель на имя, и она возвратит вам указатель нa структуру HOSTENT.

Loc:Pointer = GetHostByName( Address(Loc:MachineName) )
Структура HOSTENT часто используется для враимодействия между вами, приложением, и Winsock. Вот ее определение на C:

struct hostent { 
char FAR * h_name; 
char FAR * FAR * h_aliases;
short h_addrtype;
short h_length;
char FAR * FAR * h_addr_list; }; 

В терминах Clarion это просто группа, которая содержит три указателя и два поля типа SHORT.

Предположив что GETHOSTBYNAME() сработал, теперь мы имеем указатель на одну из таких групп. Нам интересно поле h_addr_list, которое является указателем на указатель на 4-байтную строку, содержащую IP адрем. Когда мы разименуем все эти указатели и получим 4-байтную строку, останется только конвертироваь ее в "десятично-точечный" IP адрес, который мы можем прочитать.

If Loc:Pointer <> 0
  Memcpy(ADDRESS(Loc:Ptr3),Loc:Pointer+12,4)
  If Loc:Ptr3 <> 0
    Memcpy(ADDRESS(Loc:StrPtr),Loc:Ptr3,4)
    If Loc:StrPtr <> 0
      Memcpy(ADDRESS(Loc:IPAddr),Loc:StrPtr,4)
      Loc:StrPtr = INet_NToA(Loc:IPAddr)
      If Loc:StrPtr <> 0
        LStrCpy(Loc:IPAddress,Loc:StrPtr)
        Message('IP address of machine is ' & Loc:IPAddress)
      End
    End
  End
End

Указатель является 4-байтовым целым, поэтому вначале разименуется первый указатель. Предположим, что он работает, мы делаем это снова и получаем указатель на строку. Разименовываем этот указатель, получаем 4-байтный адрес и затем вызываем функцию Winsock  INET_NTOA(). Она преобразует 4-байтный адрес в десятично-точечный IP адрес, и возвращает нам другой указатель, который мы снова разименовываем для получения окончательного IP адреса.

Получение имени по известному IP адресу

В основном, это противоположно описанному выше, но здесь есть еще несколько указателей, о которых нужно позаботится. Сначала мы преобразуеим десятично-точечный IP адрес назад в  4-байтный адрес Winsock путем вызова INET_ADDR() с указателем на десятично-точечный IP адрес. Она возврящает 4-байтный адрес (или FFFFFFFFH, в случае ошибки), который мы затем передаем функции GETHOSTBYADDR()

Эта функция возвращает указатель на строку. Мы проверяем, что вызов был успешным и, если у нас правильный указатель, затем только разьименовываем указатель и копируем первые 35 байт имени в локальную строку.

Loc:IPAddr = INet_Addr( Address(Loc:IPAddress))
  If Loc:IPAddr = InAddr_None
    ! We can't convert the address for some reason
  Else
    Loc:HostPtr = GetHostByAddr(Address(Loc:IPAddr),4,PF_INET)
    Loc:Error = WSAGetLastError()
    If Loc:Error = 0 And Loc:HostPtr <> 0 Then
      MemCpy( Address(Loc:NamePtr),Loc:HostPtr,4 )
      MemCpy( Address(Loc:MachineName),Loc:NamePtr,35)
      Message('Local machine name is ' &Clip(Loc:MachineName))
    Else
      ! Winsock couldn't look up the name for the IP address
    End
  End

Выводы

Это все. Не слишком сложно для реализации, когда понятно как использовать MEMCPY() для разименования указателей, и можно действительно производить сложные манипуляции с указателями, которые нужны для Winsock imposes.

Загрузить исходный текст


Back to my home page. Or send me mail at paula@attglobal.net

(c) 1999 Paul Attryde