
(Ebook - Pdf) Kick Ass Delphi Programming
.pdfMove(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.
Canceling a WSAAsync Operation
Because asynchronous operations run outside of normal program flow, canceling them poses a unique problem. To cancel any currently executing asynchronous operations, the Winsock API provides the WSACancelAsyncRequest function. (Note, however, that this function cannot cancel operations started by the WSAAsyncSelect function.) Listing 4.10 shows the WSACancelAsyncRequest function wrapped up inside the FAsyncType method.
Listing 4.10 Canceling an asynchronous operation
procedure TWSocket.CancelAsyncOperation(CancelOP : Boolean); begin
if WSACancelAsyncRequest(THandle(FTaskHandle)) = SOCKET_ERROR then
begin
MessageDlg(WSAErrorMsg, mtError,[mbOk],0); FStatus := Failure;
end else begin
FStatus := Success;
PostInfo('WSAAsync lookup cancelled!'); end;
end;
But the WSACancelAsyncRequest method is not visible to the RESOLVER application. So, how does RESOLVER cancel a WSAAsyncGetHostByName or WSAAsyncGetHostByAddr call? By accessing the CancelAsyncOperation method using the public boolean property,
CancelAsyncOp.
Listing 4.11 shows what happens when you press the Abort button in the NameResBox group box in RESOLVER. Because the call type is non-blocking, we assign the CancelAsyncOp to be True. This signals WSock, via the CancelAsyncOperation, to call the WSACancelAsyncRequest and thus to kill the asynchronous operation.
Listing 4.11 Signaling a cancel operation.
procedure TMainForm.AbortAsyncHostBtnClick(Sender: TObject); begin
with WSocket1 do begin
if CallType = NonBlocking then CancelAsyncOp := TRUE;
NameResBtn.Enabled := TRUE; end;
end;

Resolving Ports and Services
As with name and address resolution, we can resolve a service name and a port using either a blocking or non-blocking (asynchronous) call. The Winsock API functions getservbyname or getservbyport provide these services in blocking mode.
Looking up the port associated with a particular service type is quite similar to the process of getting the host name. For example, if we wish to find the corresponding port number for FTP, we enter FTP in the ServiceName edit control, and then assign it to the WSService property. This passes the service name to the TWSocket.SetServiceName method for conversion. This code is shown in Listing 4.12.
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.

To access the contents, click the chapter and section titles.
Kick Ass Delphi Programming
Go!
Keyword
(Publisher: The Coriolis Group)
Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin
ISBN: 1576100448
Publication Date: 09/01/96
Search this book:
Go!
-----------
Listing 4.12 Looking up a port.
procedure TMainForm.ServResBtnClick(Sender: TObject); begin
if ServiceName.AutoSelect then begin
ServiceName.AutoSelect := FALSE; PortName.AutoSelect := TRUE; with WSocket1 do
begin
if CallType = NonBlocking then begin
AsyncService := ServiceName.Text; PortName.Text := 'Pending…';
end else
WSService := ServiceName.Text; if Status = Success then PortName.Text := WSService else
PortName.Text := 'unknown service'; end;
end else begin
PortName.AutoSelect := FALSE;
ServiceName.AutoSelect := TRUE; with WSocket1 do
begin
if CallType = NonBlocking then begin
AsyncPortNo := PortName.Text;

ServiceName.Text := 'Pending…'; end
else
WSPort := PortName.Text; if Status = Success then begin
ServiceName.Text := WSPort; end
else
PortName.Text := '0'; end;
end;
end;
After copying the ReqdServiceName to ServName using the Object Pascal function StrPCopy, the protocol string is set to “TCP”, one of the required parameters for getservbyname. The protocol is set to “TCP” by default, which means that if you try to determine a port number for a service based on a different protocol, the getservbyname function would return a NIL pointer. The only way to use different protocols is to set the Protocol property in the Object Inspector to, say, “UDP”, and then recompile and re-run RESOLVER. Alternately, you can modify RESOLVER to add a protocol option.
The SetServiceName method calls the getservbyname function to get the corresponding port number. If the service is found, the getservbyname function returns a pointer to FServ, a pServent structure. This structure now holds the port number. Otherwise, the function returns a null pointer, in which case the method calls WSAErrorMsg to determine and report the cause of the error, sets the FStatus flag to Failure, and exits back to the calling application. The port number is extracted using the following statement:
FServiceName := IntToStr(ntohs(FServ^.s_port));
The string is passed back to the application by the WSService property using the following code:
if Status = Success then PortName.Text := WSService
else
PortName.Text := 'unknown service';
Figure 4.7 shows the result of the conversion.
FIGURE 4.7 The result of the service conversion.
Finding the Service
The process of translating a port number into its corresponding service is quite similar to the service lookup we just examined, except we use the blocking Winsock function getservbyport. Instead of tracing through that process, we’ll look at the use of WSAAsyncGetServByPort, the asynchronous version of getservbyport.
To use the asynchronous mode, first change the CallType property from blocking to non-blocking by selecting the Non-blocking radio button in the TypeOfLookup group box. Then enter a port number in the PortName edit control and press the Resolve button in the Service resolution group box.
The port number, in PortName.Text, is passed to the
TWSocket.SetAsyncPort method as ReqdPort when we assign it to the
AsyncPortNo property. SetAsyncPort verifies that the port number string is not empty, then copies it to FPortNo, a null terminated string. Calling WSAAsyncGetServByPort then fetches the corresponding port number.
The result of the call is stored by FTaskHandle. If FTaskHandle is zero, then the call has failed. Otherwise, the call was successful, and the SetAsyncPort exits back to the application, leaving the lookup process to continue in the background. When the lookup process is complete, AsyncOperation is invoked by a message from the Winsock DLL. The Mess variable is examined for an error. If there is no error, the method returns the port number. Otherwise, the method sets the FStatus flag to Failure, reports the error, and exits back to the application.
Resolving Protocols
Getting the protocol name and number is not as common an operation as the other conversion functions, but the functions are supported in WSock for completeness. The Winsock API functions that perform these conversions are getprotobyname, getprotobyno, WSAAsyncGetProtoByName and
WSAAsyncGetProtoByNo. The use and operation of these functions is similar to the previously discussed functions.
To Block or Not
If your application will use a local DNS, and the target host is on a local network, using blocking calls introduces far less overhead into your applications. However, if your application will connect to hosts beyond the local network, and the remote DNS is heavily used, asynchronous calls have a distinct advantage: Your application can perform other useful work while its waiting.
WSock is not a perfect Winsock component, but it provides a reasonable framework upon which to build other Internet components. Having covered the WSock component in some detail, we can now go on to build more interesting applications using our daughter components based on WSock. In the chapter that follows, we’ll build an FTP client application. After that, well, I leave it to your imagination.

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.

To access the contents, click the chapter and section titles.
Kick Ass Delphi Programming
Go!
Keyword
(Publisher: The Coriolis Group)
Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin
ISBN: 1576100448
Publication Date: 09/01/96
Search this book:
Go!
-----------
CHAPTER 5
Shopper: An FTP Client Component
JOHN PENMAN
•The FTP Protocol
•Encapsulating The FTP Protocol As A Component
•Triggering Display Output
•The Shopper Component In Operation
Go shopping for Free Stuff on the Internet with a Winsock FTP client component.
Much of the Internet’s popularity is an outgrowth of its ability to share
information among computers. The protocol that makes much of this sharing possible is the File Transfer Protocol (FTP), one of the oldest protocols in use on the Internet. Today’s FTP is formally defined in the Internet Request for Comment document RFC959.
As with other Internet protocols, FTP is cast in the classic client/server transaction model. I like to picture an FTP server as the old time shopkeeper that gets stuff from the shelves and hands them to the shopper, the FTP client. In this chapter, we’ll implement an FTP client component for Delphi named, appropriately enough, Shopper.
Shopper depends on WSock, the simple component wrapper for Winsock API calls that we developed in Chapter 4. WSock sets up the basic functionality required by FTP to communicate using TCP/IP. With those details under control, we can begin immediately to examine the FTP process.

Are You Being Served?
By convention, an FTP server always listens for a client to set up a connection on TCP port number 21. This connection, known as the control connection, remains open until either the client or server closes its side of the connection. The client and server exchange FTP commands and reply codes, respectively, over this link. Internet protocols generally use plain English text—usually in uppercase—for their commands. This holds true even for interactions between programs.
In general, for every command that is sent by the client, the server sends a 3-digit reply code, followed by either a dash or a space, and then some text. The following two lines represent typical messages:
200 PORT command successful.
230-Welcome to your I-SITE Internet server!
The dash or space following the numeric code contains important information for the client. A dash following the code tells the client that the current message is a comment destined for human eyes, and may safely be ignored. A space after the code tells the client to proceed to the next step of the action. The text that follows is usually a status message or user instruction to the user.
Figure 5.1 shows a finite state diagram representation of the interaction between the client and server during the login sequence. The FTP session begins with the client sending the USER command, followed by the user name to the server and listening for a 3-digit reply code. If the user name is valid, the server responds with code 331 or 230. An invalid user name generates a response code of 4xx or 5xx, where “xx” is the subcode for the specific error in question
FIGURE 5.1 How the client logs in to the server
A response of 230 indicates that the user name is valid and no further information is needed to access the system. This server typically issues this code in response to the famous “anonymous” FTP login. A response of 331 indicates that the user name is valid, but a password is required. In this case, the client next sends the PASS command, followed by the password.
An incorrect password generates a 4xx or 5xx response, indicating an error. If the password is accepted by the server, the server may send a 230 code to indicate that the login sequence is complete. Alternately, if an account is required for login, the server sends another 331 code to tell the client to send the ACCT command and information.
Once it establishes a successful connection, the client can continue to issue commands. However, if there is a problem, such as a bad command syntax, or there are too many users logged on to the system, the server sends a 4xx or 5xx

code and severs the connection.
The Shopper Component
Shopper descends from the WSock VCL component developed in Chapter 4, and uses WSock’s TWSocket class for handling mundane housekeeping chores such as loading of the Winsock DLL, filling the data structures for setting up a connection with a host, transfer of data, ending the connection with the server, and closing down Winsock.
The foundation WSock VCL has its Service property set to “NoService”. As Shopper will always be an FTP client component, we set the Service property to “FTP” in the TShopper.Create constructor. The FTP protocol uses the same WSock default settings—great things, these components! As shown in Figure 5.2, Shopper has eight other properties in addition to Service:
AddrType, Protocol, SockType, CallType, LogOn, UserName, Password, and HomeServer.
FIGURE 5.2 Object Inspector showing the properties of Shopper.
Shopper implements the more useful FTP commands, including USER, PASSWORD, RETR, and PUT, as individual properties. These FTP properties are in the public section of TShopper, where they are available to the component user. Each of these methods relies on the FTPCommand procedure, the heart of the Shopper component. FTPCommand is a simple parser implemented as a large case statement. What the expression lacks in elegance, however, it makes up for in simplicity.
Because the SHOPPER.PAS source file for the component in its entirety is over 1600 lines long, we won’t be publishing it here within the chapter. Selected excerpts will be used to clarify certain points about its operation, and you can always print the full file from the CD-ROM for more leisurely perusal.
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.

To access the contents, click the chapter and section titles.
Kick Ass Delphi Programming
Go!
Keyword
(Publisher: The Coriolis Group)
Author(s): Don Taylor, Jim Mischel, John Penman, Terence Goggin
ISBN: 1576100448
Publication Date: 09/01/96
Search this book:
Go!
-----------
Displaying Output
Although Shopper is a non-visual component, it must occasionally communicate with the user’s application to display messages passed between the client and the server. TShopper’s published property OnChangeInfo (descended from the TWSocket class in WSock) and private access procedure Change meet this need. The following code fragment illustrates the Change procedure:
procedure TWSocket.Change; begin
if Assigned(FOnChange) then FOnChange(Self);
end;
When a message is sent or received over the control connection, TempStr in FTPCommand sets the Info property, and then FTPCommand calls the Change procedure. Inside Change, Assigned returns True and the Shopper1Change procedure in the application displays Info.
To enable this communication between Shopper and the client application, I created the
Shopper1ChangeInfo procedure by using the Events page in the Object Inspector. The
StatusMemo window, in which these messages are displayed, gets updated whenever the
FOnChange event occurs. Shopper1ChangeInfo contains the following code:
procedure TMainForm.Shopper1ChangeInfo(Sender: TObject); begin
StatusMemo.Lines.Add(Shopper1.Info);
end;
Putting Shopper to Work
FTPCLI, a basic FTP application created using the Shopper component, is shown in Figure 5.3. To create this application, start a new project, FTPCLI, call the main form MainForm, and store this in MAIN.PAS, shown in Listing 5.1. Be sure to install the WSock and Shopper components in the palette, and then pluck the Shopper component from the palette onto the main form. Add a button for each FTP command to the form. For example, the Open button calls up Shopper1.Start as shown here: