• No results found

Running the Resolver Application

The RESOLVER program is a Winsock database conversion program that employs a few of the more interesting methods and properties provided by TWSocket.

RESOLVER can resolve a host name to its Internet (IP) address, and vice versa.

Given either a port number or a service name, it can derive the other. And it can translate between protocol numbers and protocol names. These are practical demonstrations, for the resolution of a host name and a service name are the most common operations that a Winsock application ever performs.

The complete application as it appears in the Delphi IDE is shown in Figure 4.1.

Click on the WSocket1 component and you’ll see its properties in the Object Inspector, as shown in Figure 4.2. The default values, shown here, are fine for performing resolutions using blocking functions. The Service property is set to its default value of NoService, as there is no specific service to perform resolution tasks in our application.

FIGURE 4.1 The RESOLVER application.

FIGURE 4.2 The WSocket properties display.

Figure 4.3 shows the Events page on which there are two event handlers. Whenever the status of the Winsock DLL changes, the WSocket1ChangeInfo event procedure passes the information from WSock to the application. Similarly, the

WSocket1AsyncDone event procedure posts information whenever an asynchronous function completes its task.

FIGURE 4.3 The WSocket events display.

When you run the RESOLVER application, the Application.CreateForm procedure in RESOLVER.DPR calls the constructor TWSocket.Create to set WSock’s

properties to their default settings. After the constructor initializes the component and calls the Winsock DLL successfully, the TMainForm.FormCreate procedure performs several tasks, as shown in Listing 4.5.

Listing 4.5 The main form’s FormCreate procedure

procedure TMainForm.FormCreate(Sender: TObject);

begin

with WSocket1 do begin

MachineName.Text := LocalName;

VendorName.Text := WSVendor;

VersionNo.Text := WSVersion;

MaxNoSockets.Text := WSMaxNoSocks;

MaxUDPacketSize.Text := WSMaxUDPPSize;

WSStatusInfo.Text := WSStatus;

CallType := Blocking;

end;

if WSocket1.CallType = Blocking then begin

AbortAsyncHostBtn.Enabled := FALSE;

AbortAsyncServBtn.Enabled := FALSE;

AbortAsyncProtoBtn.Enabled := FALSE;

end;

HintBtn.Checked := TRUE;

MainForm.ShowHint := TRUE;

end;

Previous Table of Contents Next

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

Brief Full Advanced Search

Search Tips

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Previous Table of Contents Next

What’s My Name?

RESOLVER reports the name that your machine presents to the network. It can do so because the WSocket1.LocalName property assigns the your machine’s name to Machine.Text. The TWSocket.GetLocalName method acts as a wrapper for the Winsock API’s gethostname routine. It retrieves your machine’s name from the local host’s file (typically located in the Windows directory) and returns it in the LocalName property.

Listing 4.6 shows the code for the TWSocket.GetLocalName method in WSOCK.PAS. It’s important to note that gethostname, as with all Winsock routines, handles only ASCII strings.

The GetLocalName method uses the StrPas function to convert the ASCII string to an Object Pascal. The machine name then appears in the MachineName edit control. If your machine is nameless, GetLocalName simply returns a blank string. Similarly, the miscellaneous

information gathered by TWSocket.StartUp about the particular Winsock DLL in use is passed back to RESOLVER by the properties FVendor, FWSVersion, FWSStatus, FMaxNoSocks, and FMaxUDPPSize for display in the WSInfoBox group box.

Listing 4.6 The GetLocalName function

function TWSocket.GetLocalName : String;

var

LocalName : array[0..MaxBufferSize] of Char;

begin

if gethostname(LocalName, SizeOf(LocalName)) = 0 then begin

Result := StrPas(LocalName);

end else begin

Result := '';

end end;

What’s the Address?

The most common operation performed by a Winsock application is resolving a host name in

Go!

Keyword

---Go!

blocking mode. In this case, blocking means that the application is waiting for a response from a remote computer—a response that may never come. Unable to proceed or respond to input until it receives a response, a blocked application often appears “dead” to the user.

Under Unix, this type of operation poses little problem. Even if an application blocks, the pre-emptive nature of Unix allows other applications to operate normally. Windows 3.1, in contrast, implements only cooperative multitasking. Executing a blocking Winsock operation would lock the brakes on your Windows system.

To allow Windows to continue to execute in a blocking situation, Winsock replaces each blocking functions with a pseudo-blocking asynchronous equivalent. Instead of blocking, these routines enter a polling loop while waiting for the network event to complete. These

non-blocking routines are identified by the WSAAsync prefix before the name. For example, WSAAsyncGetHostByName is the asynchronous version of gethostbyname.

Typically, an Internet host identifies itself over the network with a unique address in the form of a dotted decimal number quadruple such as 127.0.0.1. (Note that this is the special loopback address that you can use to test your Winsock applications on a non-networked machine.) Although highly convenient for computers, these addresses hold little appeal for humans. To reconcile this problem, a system was implemented that allows the creation of a unique

human-readable name for each Internet address. For example, the name slipper109.iaccess.za is equivalent to the Internet address 196.7.7.109.

To resolve a host name, enter the name in RESOLVER’s Host edit control. After you press the Resolve button in the Name Resolution group box, RESOLVER assigns the name that you gave in the Host.Text control to the Hostname property. Then the property calls

TWSocket.SetRemote HostName. Listing 4.7 shows how WSock handles this. If the NameReqd string is empty, SetRemoteHostName reports the error and exits. Otherwise, StrpCopy is used to convert FRemoteName from a Pascal string to an ASCII string.

Listing 4.7 Mapping a host name to its Internet address

procedure TWSocket.SetRemoteHostName(NameReqd : String);

if Length(FRemoteName) = 0 then begin

FStatus := Failure;

MessageDlg('No host name given!', mtError,[mbOk],0);

Exit;

end;

PostInfo('Resolving host');

StrPCopy(FpHostName, FRemoteName);

{ check what type of address has been entered } IPAddress := inet_addr(FpHostName);

if IPAddress <>INADDR_NONE then {this is a dotted address}

begin

FAddress := IPAddr;

P := addr(IPAddress);

case AddrType of

AFINET : FHost := GetHostByAddr(P, 4, AF_INET);

end;

end

else {no, it looks like a human readable hostname}

begin

FAddress := HostAddr;

FHost := GetHostByName(FpHostName);

end;

if FHost = NIL then

begin{Unknown host, so aborting…}

if OkToDisplayErrors then

MessageDlg('Unknown host', mtError,[mbOk],0);

FStatus := Failure;

Exit;

end;

PostInfo('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);

PostInfo('Host found…');

end;

end;

Next, the SetRemoteHostName method checks whether the string already contains a numeric Internet address using the inet_addr function. If not, the method assumes that the string contains a host name and calls the gethostbyname function to resolve it to an IP address. If the host name is not present in the local hosts file, gethostbyname looks for the name in a foreign hosts file elsewhere on the network.

If the name is not found, the lookup process times out and sets the private property FHost, which is a pHostent structure, to NIL. Then, SetRemoteHostName posts an error message, sets the FStatus flag to Failure, and exits back to the calling application. If the IP address is found, however, the gethostbyname function returns a pointer to FHost, which contains the IP address.

The SetUpAddress procedure then extracts the IP address from the FHost structure. Finally, SetRemoteHostName sends back the dotted address as a Pascal string using the following statement:

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

Previous Table of Contents Next

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

Brief Full Advanced Search

Search Tips

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming (Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Previous Table of Contents Next

The inet_ntoa function converts the returned IP address to an ASCII string in dotted format. The StrPas function finishes the conversion to a Pascal string. The address information for the socket is placed in FSockAddress, where it will later be used to set up a connection with a host machine.

Setting the Hostname property writes the IP address to the IPName edit control, as shown in Figure 4.4.

FIGURE 4.4 RESOLVER after resolving a host name.

What’s Your Name?

RESOLVER can also derive the name of a host from its numeric Internet address. The process begins when you enter an address into the IPName edit control, as shown in Figure 4.5. When you click the Resolve button, RESOLVER passes the address string in IPName.Text to the

SetRemoteHostName method via the Hostname property.

FIGURE 4.5 A dotted IP address ready to resolve.

As before, the SetRemoteHostName method uses the inet_addr function to check whether the string is in valid Internet address form. Before calling this function, however, the method assigns the address of the IPAddress string to a pointer, P, which gethostbyaddr requires as one of its parameters.

If inet_addr returns a result other than INADDR_NONE (meaning that the string is a numeric Internet address), SetRemoteHostName calls gethostbyaddr. Like the call to gethostbyname, this call may also block. If gethostbyaddr is successful, it returns a pointer to the pHostent structure. If no corresponding name is found for the IP address, FHost is set to NIL and SetRemoteHostName reports the error, sets the FStatus flag, and exits. The Hostname property writes the host name obtained, using the statement below, back to the Host edit control:

Go!

Keyword

---Go!

FRemoteName := StrPas(FHost^.h_name);

Getting the Name Asynchronously

Using the blocking lookup functions gethostbyname and gethostbyaddr is fairly straightforward.

Employing the asynchronous versions of these functions, WSAAyncGetHostByName and WSAAsyncGetHostByAddr, is a little more complex. To understand the asynchronous process, let’s go through the steps of calling WSAAsyncGetHostByName from the RESOLVER program.

First, change the CallType property from Blocking to NonBlocking by selecting the NonBlocking radio button in the TypeOfLookup group box as shown in Figure 4.6. Pressing the Resolve button now assigns the name to the AsyncHostName property and passes it to the SetAsyncHostName procedure as shown in Listing 4.8.

FIGURE 4.6 Changing from blocking to non-blocking.

Listing 4.8 Resolving the host name.

procedure TWSocket.SetAsyncHostName(ReqdHostName : String);

var

Size : PInteger;

P : Pointer;

IPAddress : TIn_addr;

SAddress: array[0..31] of char;

sa : Tin_addr;

begin

FAsyncRemoteName := ReqdHostName;

if Length(FAsyncRemoteName) = 0 then begin

FStatus := Failure;

MessageDlg('No host name given!', mtError,[mbOk],0);

Exit;

end;

StrPcopy(SAddress, FAsyncRemoteName);

IPAddress.s_addr := inet_addr(SAddress);

if IPAddress.s_addr <> INADDR_NONE then {this is a dotted address}

begin

FAddress := IPAddr;

FAsyncType := AsyncAddr;

if IPAddress.s_addr <> 0 then

FTaskHandle := WSAAsyncGetHostByAddr(FAsyncHWND, ASYNC_EVENT, pChar(@IPAddress), 4, PF_INET,

@FAsyncBuff, SizeOf(FAsyncBuff));

if FTaskHandle = 0 then begin

MessageDlg(WSAErrorMsg,mtError,[mbOk], 0);

FStatus := Failure;

if FNoOfBlockingTasks > 0 then dec(FNoOfBlockingTasks);

Exit;

end else FStatus := Success;

end

else {no, it looks like a human readable hostname}

begin

FAddress := HostAddr;

FAsyncType := AsyncName;

Inc(FNoOfBlockingTasks);

FTaskHandle := WSAAsyncGetHostByName(FAsyncHWND, ASYNC_EVENT, @FpHostName, @FAsyncBuff, MAXGETHOSTSTRUCT);

if FTaskHandle = 0 then begin

MessageDlg(WSAErrorMsg,mtError,[mbOk], 0);

FStatus := Failure;

if FNoOfBlockingTasks > 0 then dec(FNoOfBlockingTasks);

Exit;

end else FStatus := Success;

end;

end;

SetAsyncHostName calls the WSAAsyncGetHostByName procedure with five important

arguments. FASyncHWND is a handle to the window in which the asynchronous function will post its message on completion of the lookup operation. This window handle is initialized in the

TWSocket.Create constructor by a call to AllocateHWND with AsyncOperation as its procedural parameter. ASYNC_EVENT is the event notification constant used by

WSAAsyncGetHostByName. FAsyncBuff is an array of characters that holds the result of the operation. Finally, MAXGETHOSTSTRUCT is a Winsock constant representing the maximum size of the FAsyncBuff buffer. The WSAAsyncGetHostByName procedure returns the task number of the call as a TaskHandle type that is assigned to FTaskHandle.

WSAAyncGetHostByName returns immediately with a value of 0 if the call was unsuccessful or greater than 0 if successful. However, a non-zero value for FTaskhandle means only that the call to WSAAyncGetHostByName succeeded, not that the subsequent lookup operation (which continues to execute in the background) will be successful.

When the lookup does complete, the Winsock DLL triggers a ASYNC_EVENT event, notifying the AsyncOperation procedure that it should examine the ASYNC_EVENT message, as shown in Listing 4.9.

Previous Table of Contents Next

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

Brief Full Advanced Search

Search Tips

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Previous Table of Contents Next

Listing 4.9 The AsyncOperation procedure

procedure TWSocket.AsyncOperation(var Mess : TMessage);

var

MsgErr : Word;

begin

if Mess.Msg = ASYNC_EVENT then begin

MsgErr := WSAGetAsyncError(Mess.lparam);

if (MsgErr <> 0) and (StrLen(FpHostName) > 0) then begin

FStatus := Failure;

MessageDlg(WSAErrorMsg,mtError,[mbOk], 0);

Exit;

end else begin

FStatus := Success;

PostInfo('WSAAsync operation succeeded!');

case FAsyncType of

end;

end;

end; {case}

if FNoOfBlockingTasks > 0 then dec(FNoOfBlockingTasks);

end;

end;

end;

The WSAGetAsyncError macro checks the Mess variable. If it indicates that an error occurred,

AsyncOperation calls WSAErrorMsg to display the cause of the error, then exits with the FStatus flag set to Failure. If no error has occurred, we parse the FAsyncType variable.

When we called WSAAyncGetHostByName, we set the FAsyncType to AsyncName to indicate that we were performing an asynchronous name lookup. The case statement now branches based on the value of FAsyncType to the AsyncName clause. There, the character array FAsyncBuff, containing the result of the lookup, is typecast to a pHostent structure and stored in FHost. The address structure for the resolved host is read by SetUpAddress to get the corresponding IP address. AsyncChange calls GetAsyncHostName to return the IP address back to RESOLVER.

Previous Table of Contents Next

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc.

All rights reserved. Reproduction whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Read EarthWeb's privacy statement.

Brief Full Advanced Search

Search Tips

To access the contents, click the chapter and section titles.

Kick Ass Delphi Programming

(Publisher: The Coriolis Group)

Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin ISBN: 1576100448

Publication Date: 09/01/96

Search this book:

Previous Table of Contents Next

Who’s at This Address?

To further illustrate the use of asynchronous mode, we’ll examine how the

WSAAyncGetHostByAddr function (shown in Listing 4.8) retrieves a host name when given only an Internet address. To use this function in the RESOLVER application, set the CallType property to NonBlocking in the TypeOfLookUp group box, and enter an Internet address in the IPName edit control.

As before, we assign the name to the AsyncHostName property for handling by the TWSocket.SetAsyncHostName method. If the name we passed is an empty string,

SetAsyncHostName sets the FStatus flag to Failure, posts an error message, and exits. After establishing that the FAsync RemoteName is not empty, we call the inet_addr function to determine whether the string is a dotted decimal Internet address or host name. A return value different than INADDR_NONE indicates the string is in Internet address format.

This string is then passed to WSAAyncGetHostByAddr to get the host information for the Internet address. A successful call to WSAAync GetHostByAddr sets the FTaskHandle to a number greater than zero, but doesn’t ensure that we will get a valid result from

WSAAyncGetHostByAddr on completion. The method exits back to the RESOLVER application, and the lookup continues in the background.

When the lookup operation completes, the Winsock DLL posts a message to WSock by

triggering the ASYNC_EVENT event. This trigger wakes up the TWSocket.AsyncOperation method, which examines the Mess variable. If Mess contains an error, the AsyncOperation method calls WSAErrorMsg to determine the error, sets the FStatus flag to Failure, and exits.

If the Mess variable contains no error, a case statement parses FAsyncType. In this example, FAsyncType has the value AsyncAddr, so the same portion of code executes that handled the AsyncName case. Next, we parse FAddress to execute the section of code that handles the result of WSAAyncGetHostByAddr. This setting is automatically determined by the

SetAsyncHostName method by using the result of the inet_addr operation. That is, FAddress is set to IPAddr when a dotted decimal address is found, otherwise it is set to HostAddr for a host name. The host name is then extracted by the following code:

Go!

Keyword

---Go!

Move(FHost^.h_addr_list^, Fh_addr, SizeOf(FHost^.h_addr_list^));

FAsyncRemoteName := StrPas(FHost^.h_name);

This result is posted back to the application via the AsyncChange procedure.