Delphi 3. Библиотека программиста

       

Какой у тебя адрес?


Преобразование имени хоста является самой распространенной операцией, выполняемой Winsock-приложениями в режиме блокировки. В данном случае «режим блокировки» означает, что приложение ожидает ответа от удаленного компьютера — ответа, который может никогда не прийти. До получения ответа заблокированное приложение не может продолжать работу или реагировать на ввод информации пользователем и часто кажется «мертвым».

В таких операционных системах, как Unix, Windows 95 и Windows NT, такое поведение не представляет особых проблем. Даже если приложение заблокировано, использованный в них принцип вытеснения задач позволяет другим приложениям нормально работать.

Чтобы пользователь не терял возможности взаимодействовать с любым приложением Winsock во время блокировки, Winsock заменяет блокирующие функции псевдоблокирующими асинхронными эквивалентами. Вместо того чтобы осуществлять полноценную блокировку, эти функции при ожидании завершения сетевого события переходят в цикл опроса. Псевдоблокирующие функции можно узнать по префиксу WSAAsync. Например, функция WSAAsyncGet HostByName является асинхронной версией gethostbyname. Используя WSAAsyncGet HostByName, пользователь может в любой момент прервать операцию просмотра. В блокирующих функциях такая возможность отсутствует.

Чтобы изменить поведение RESOLVER32, достаточно сменить значение свойства Access c Blocking на NonBlocking, или наоборот. Значение NonBlocking сообщает CsSocket о том, что для просмотра должны использоваться асинхронные функции.

Обычно хост Internet идентифицируется в сети по уникальному адресу
в виде четверки десятичных чисел, разделенных точками, — например, 127.0.0.1 (обратите внимание на этот специальный адрес обратной связи, с его помощью можно тестировать приложения Winsock на компьютерах, не подключенных к сети). Хотя такие адреса исключительно удобны для компьютеров, на людей они производят угнетающее впечатление. Чтобы уладить эту проблему, была разработана система, которая позволяет задать уникальное символьное имя для каждого IP-адреса. Например, имя slipper109.iaccess.za соответствует IP-адресу 196.7.7.109.

Чтобы преобразовать имя хоста, введите его в текстовом поле edHostName программы RESOLVER32. После нажатия кнопки Resolve RESOLVER32 присваивает имя, введенное в edHostName, свойству Hostname. При этом свойство вызывает метод TCsSocket.SetRemoteHostName. Если строка NameReqd пуста, SetRemote HostName сообщает об ошибке и завершается. В противном случае CsSocket проверяет значение поля FAccess (которое может быть равно Blocking или NonBlocking в зависимости от свойства Access), чтобы определить режим преобразования имени хоста в IP-адрес. Если значение FAccess равно NonBlocking, вызывается SetAsyncHostName. В противном случае функция StrpCopy преобразует FRemoteName из строки Паскаля в строку с нуль-терминатором. В листинге 5.7 показано, как это делается в CsSocket.

Листинг 5.7. Метод TCsSocket.SetRemoteHostName — преобразование
имени хоста в IP-адрес

procedure TCsSocket.SetRemoteHostName(NameReqd : String); var P : Pointer; IPAddress : LongInt; begin FRemoteName := NameReqd; if Length(NameReqd) = 0 then begin FStatus := Failure; ErrorEvent(FStatus, 'No host name given!'); case FLookUpOp of resHostName : LookUpEvent(resHostName, FRemoteName, FALSE); resIPAddress : LookUpEvent(resIPAddress, FRemoteName, FALSE); end;// case raise ECsSocketError.create('No host name given!'); Exit; end; if FAccess = NonBlocking then SetAsyncHostName(FRemoteName) else begin InfoEvent('Resolving host'); StrPCopy(FpHostName, FRemoteName); { Определяем тип введенного адреса } IPAddress := inet_addr(FpHostName); if IPAddress <>INADDR_NONE then { Это IP-адрес } begin FLookUpOp := resHostName; FAddress := IPAddr; P := addr(IPAddress); case AddrType of AFINET : FHost := gethostbyaddr(P, 4, AF_INET); end; end else { Нет, это больше похоже на символьное имя хоста } begin FLookUpOp := resIPAddress; FAddress := HostAddr; FHost := gethostbyname(FpHostName); end; if FHost = NIL then begin{ Неизвестный хост, отменяем попытку...} LookUpEvent(FLookUpOp, '', FALSE); FStatus := Failure; if FOKToDisplayErrors then raise ECsSocketError.create('Unable to resolve ' + FpHostName); Exit; end; InfoEvent('Host found'); FStatus := Success; Move(FHost^.h_addr_list^, Fh_addr, SizeOf(FHost^.h_addr_list^)); if FAddress = HostAddr then begin SetUpAddress; FRemoteName := StrPas(inet_ntoa(FSockAddress.sin_addr)); end else if FAddress = IPAddr then begin FRemoteName := StrPas(FHost^.h_name); InfoEvent('Host found...'); end; case FLookUpOp of resHostName : LookUpEvent(resHostName, FRemoteName, TRUE); resIPAddress : LookUpEvent(resIPAddress, FRemoteName, TRUE); end;// case end; end;

Затем метод SetRemoteHostName с помощью функции inet_addr проверяет, не содержит ли исходная строка числового IP-адреса. Если не содержит, метод предполагает, что в ней находится имя хоста, и вызывает функцию gethostbyname для преобразования его в IP-адрес. Если имя хоста отсутствует в локальном файле хостов, gethostbyname ищет имя в удаленном файле хостов, хранящемся в сети.

Если имя не найдено, процесс поиска прекращает работу по тайм-ауту и присваивает protected-свойству FHost (которое представляет собой указатель на структуру pHostent) значение NIL. Затем SetRemoteHostName вызывает обработчик события LookUpEvent, чтобы сообщить о неудачном завершении просмотра, присваивает флагу FStatus значение Failure и возвращает управление вызывающему приложению. При удачном завершении поиска функция gethostbyname возвращает указатель на FHost, где содержится найденный адрес. Наконец, SetRemoteHostName возвращает IP-адрес в виде строки Паскаля, для чего используется следующий оператор:

FRemoteName := StrPas(inet_ntoa(FSockAddress.sin_addr));

Функция inet_itoa переводит возвращаемый IP-адрес в строку с нуль-терминатором, а функция StrPas завершает преобразование в строку Паскаля. Адресная информация сокета размещается в поле FSockAddress, откуда она позднее извлекается для установки соединения с хостом. Полученный в результате поиска IP-адрес помещается в текстовое поле edIPName (см. рис. 5.4). Для этого RESOLVER32 использует обработчик события OnLookUp, который вызывается внутри процедуры LookUpEvent. В листинге 5.8 показано, как это делается.

Рис. 5.4. RESOLVER32 после преобразования имени хоста

Листинг 5.8. Метод TfrmMain.CsSocket1Lookup, используемый
программой RESOLVER32 для отображения результатов,
полученных от функции просмотра

procedure TfrmMain.CsSocket1Lookup (Sender: TObject; LookUpOp: TLookUpOp; Value: String; Result : Boolean); begin btnResolve.Enabled := TRUE; btnAbortRes.Enabled := FALSE; Screen.Cursor := crDefault; if Result then begin pnStatus.Color := clLime; case LookUpOp of resHostName : begin edHostName.Text := Value; pnStatus.Caption := 'IP address resolved'; end; resIPAddress : begin edIpName.Text := Value; pnStatus.Caption := 'Host name resolved'; end; resService : begin edPortName.Text := Value; pnStatus.Caption := 'Service resolved'; end; resPort : begin edServiceName.Text := Value; pnStatus.Caption := 'Port number resolved'; end; resProto : begin edProtoNo.Text := Value; pnStatus.Caption := 'Protocol resolved'; end; resProtoNo : begin edProtoName.Text := Value; pnStatus.Caption := 'Protocol number resolved'; end; end;// case end else begin pnStatus.Color := clRed; case LookUpOp of resHostName : begin edHostName.Text := ''; pnStatus.Caption := 'IP address resolution failed.'; end; resIPAddress : begin edIpName.Text := ''; pnStatus.Caption := 'Host name resolution failed'; end; resService : begin edPortName.Text := ''; pnStatus.Caption := 'Service resolution failed'; end; resPort : begin edServiceName.Text := ''; pnStatus.Caption := 'Port number resolution failed.'; end; resProto : begin edProtoNo.Text := ''; pnStatus.Caption := 'Protocol resolution failed.'; end; resProtoNo : begin edProtoName.Text := ''; pnStatus.Caption := 'Protocol number resolution failed.'; end; end;// case end; end;

Содержание раздела