Tutorial

A S T A    M P 3    S H A R I N G

Chris Weston
March, 2001

Copyright 2001 by ASTA Technology Group. All rights reserved. 

 

Accompanying Source Code for the Tutorial:

Client Source

Server Source

Seems like almost everyday you hear or see something in the media about Napster. A Peer-to-Peer file-sharing concept giving the recording industry fits.  The concept is simple.  Centralize a database for client’s to reference a listing files that each user is willing to share along with an address to that particular user.  Once a file is selected from a client, open up a Peer-to-Peer connection between the two clients and pass requested file.  This has ASTA written all over it!  With the tools that are provided with in ASTA this tutorial will show you how to do just this using minimal code.  We’re not promoting copyright infringement by any means.  We’re just showing you how easy it is to create this type of software with the tools that ASTA provides. 

This tutorial will cover several ASTA techniques including Server Methods, Peer-to-Peer connectivity between clients using the TAstaServerSocket and the TAstaClientSocket, TAstaParamLists and some very useful functionality that is included in AstaUtil.pas.

Our Asta Server will be coded to maintain a list of files that each client has to offer along with the current IP address of each client.  On the client side we need to send the list of files to the server, create a way to establish a Peer-to-Peer connection between each client and build a process for sending files.  Figure 1 displays a layout of our connectivity.


Figure 1

 

Now that we know what we need to accomplish lets dig in on the server coding. 

The first thing that you need to do is make sure that you have placed the Mp3Listing.db paradox file into your directory that the server executable resides in. To keep the database table with a fresh listing of the files available we will code server methods that the client can pass parameters to and execute each time a connection to the server is made.  To get started click on TAstaBusinessObjectsManager  within the AstaDataModule. In the Object Inspector click on the ellipse to see the actions that have been coded as shown in Figure 2.


Figure 2.

This will bring up a window containing all the actions that have been assigned to AstaBusinessObjectManager1.  We have coded two methods SearchList and Update Files.  The first Action, SearchList, is called by the client to retrieve a list of available files with in our database. The following properties have been set for this method:


Figure 3

The method has been defined as SearchList and we set the DataSet to qryMp3Listing.  QryMp3Listing is a normal TQuery that we have setup to connect to the Mp3Listing table placed into the directory this server will reside.  When the client executes this method it will be sending a parameter which we will use to build an sql statement for our dataset.  For this we define one param called SearchCritera with a ParamType of ptInput and DataType of ftString.


Figure 4

Next we added the following code to the OnAction event:

procedure TAstaDataModule.AstaBusinessObjectManager1Actions0Action(
  Sender: TObject; ADataSet: TDataSet; ClientParams: TAstaParamList);

var
 
SearchCriteriaText : String;

begin

  //Assign the Param sent by the client to SearchCriteriaText and convert to Uppercase for later use. 
 
SearchCriteriaText := UpperCase(ClientParams[0].AsString);

  //Close the Dataset.
  if aDataSet.Active then aDataSet.Close;

  //Assign sql to Dataset based on Param sent by the client.
  (aDataSet as TQuery).sql.Text := 'Select * from Mp3Listing Where Upper(FileName) Like "%'+SearchCriteriaText+'%"';

  //Log activity on the server

  with TAstaActionItem(sender) do begin
    ServerSocket.recordServerActivity(ClientSocket, 'Using Database ' + TTable(DataSet).SessionName);

    ServerSocket.recordServerActivity(Clientsocket, 'Business Object Incoming Params (Search Criteria)' + SearchCriteriaText);

  end;

  //Open the dataset.
  aDataSet.Open;

end;

When this Method is called by the client, the SearchCriteria param is passed to the server and the OnAction event is triggered.  We assign the Parameter sent by the client to the string “SearchCriteriaText”.  The dataset (qryMp3Listing) is closed and sql is assigned to the dataset.  Next we log the client activity on the server then open the dataset with the new sql based on the Params sent by the client. 

The next method is the UpdateFiles method.  This method will be used to send a list of files that the user has available for upload through Peer-to-Peer connectivity.  We set the properties for this method as follows in figure 5.


Figure 5

When we call this method from the client we’ll be sending a list of files and the IP address of the client in the form of Params so we add two as shown in Figure 6.

 
Figure 6

Just as we coded the first method in the OnAction event we do the same for this method by adding the following code:

procedure TAstaDataModule.AstaBusinessObjectManager1Actions1Action(
  Sender: TObject; ADataSet: TDataSet; ClientParams: TAstaParamList);

var
  FileList : TStringList;
  ct : Integer;

begin

  //Assign the Param sent by the client to SearchCriteriaText and convert to Uppercase for later use.
  FileList := TStringList.Create;
  FileList.Text := ClientParams[0].AsString;
 

  //Open the dataset.
  aDataSet.Open;

  for ct := 0 to FileList.Count -1 do
    begin
      with (aDataSet as TQuery) do
        begin
          Insert;
          FieldByName('FileName').AsString := FileList.Strings[ct];
          FieldByName('Host').AsString := ClientParams[1].AsString;
          Post;
        end;
    end;

  //Log activity on the server
  with TAstaActionItem(sender) do begin
    ServerSocket.recordServerActivity(ClientSocket, 'Using Database ' + TTable(DataSet).SessionName);
    ServerSocket.recordServerActivity(Clientsocket, 'Business Object Incoming Params (File List) ' + FileList.Text);
  end;

  FileList.Free;

end;

When the client calls this method it sends two parameters to the server.  The first is a list of files in the form of a string that we assign to FileList.  We then open the dataset and loop through each line within the FileList and insert a record into the dataset with the File Name and Host which is the IP address stored in the second parameter.  We then log the activity on the server and free our FileList that was created.

We now have two of the three functions coded with in our server.  We have a way to search our database for available files based on input from the client and we also have a way to update the list with files available from the client for Peer-to-Peer file transfer. 

The last thing that we need to do is create some code on the server side to “Clean up” the file list when the client disconnects.  To do this we’ve added the following code into the OnUserListChange event on the AstaServerSocket:

  //Sneak in and clear files user had available;
  AstaDataModule.qryCleanUpMp3Listing.Close;
  AstaDataModule.qryCleanUpMp3Listing.SQL.Text := 'Delete from Mp3Listing Where Host = "'+Socket.RemoteAddress+'"';
  AstaDataModule.qryCleanUpMp3Listing.ExecSQL;

All we’re doing here is clearing out all the records where the Host field matches the IP address of the machine that is disconnecting.  QryCleanUpMp3Listing is a standard TQuery with the DatabaseName set to our DBDEMOS alias and the request live property set to true.

We now have a functional server compile it, run it and move on to the client side.

For the client we’ve created a very simple interface using a TPageControl with 3 tabs as shown in figure 7.


Figure 7

The first tab contains our search area with a Tedit used for the search criteria, two TButtons for searching and downloading and a TDBGrid for displaying the search results.  I’ve also added a TMediaPlayer for your listening pleasure.  For the other two Tabs, Downloads and Uploads, we use a TDriveComboBox, TDirectoryListBox and TfileListBox.

Figure 8 shows us the Asta components used with in the client.  2 TAstaClientSockets,   1 TAstaServerSocket and 2 TAstaClientDataSets.


Figure 8

The first thing we’re going to do is code the first TAstaClientSocket and the two TAstaClientDataSets to work with the server we just finished.  Properties for the TAstaClientSocket are left as the defaults which is shown in Figure 9.


Figure 9

Next we code the first TAstaClientDataSet to execute the SearchList method we coded on the server.  To do this we set the ServerMethod property to AstaDataModule.SearchList.  Notice that once you set this property you now have 1 Param set automatically for you.  This is the SearchCriteria Param that we setup on the server side.   Since our server method will be returning a dataset we don’t have to mess with anything else.  Are you starting to see the benefits of server side coding yet?


Figure 10

To view the results of our dataset we place a TDataSource and assign the dataset to AstaClientDataSet1.  Next we set the DBGrid’s DataSet property to AstaClientDataSet1.  On the button labeled “Search” we place the following code:

procedure TfrmMain.SearchClick(Sender: TObject);

begin

//Close the DataSet
AstaClientDataSet1.Close;

//Assign the Param
AstaClientDataSet1.Params[0].AsString := Edit1.Text;

//Open the DataSet
AstaClientDataSet1.Open;

end;

All we need to do is close the dataset, assign the param and then open the dataset.  The contents of Edit1 is then sent to the server as the SearchCriteria param that we set up in the SearchList server method.  The server then builds and sql statement based on this param and returns the results back to the AstaClientDataSet which are displayed within the DBGrid.

Our next step is to code the second TAstaClientDataset to work with the second method, UpdateFiles.  We assign the same properties as the first AstaClientDataSet with exception of the ServerMethod which will we assign to AstaDataModule.UpdateFiles.  Notice again that the two params that we setup on the server automatically come in.  Our next step is to create some code that we can call to execute this method.  A procedure called SendSharedFileList has been created and coded as follows:

procedure TfrmMain.SendSharedFileList;

var
  tmpList : TStringList;

begin

//Create
tmpList := TStringList.Create;

//Assign
tmpList.Assign(FileListBox2.Items);

AstaClientDataSet2.Close;

//Assign Params
AstaClientDataSet2.Params[0].AsString := tmpList.Text;
AstaClientDataSet2.Params[1].AsString := IPList.Strings[0];

//Open the DataSet
AstaClientDataSet2.Open;

tmpList.Free;

end;

What we’re doing with this code is loading a StringList with the list of file names that the TfileListBox in the Upload folder is displaying, assigning the Params for the UpdateFiles Server Method and executing the method on the server.  The IPList.Strings[0] contains the IP Address of the current machine.  This code can be found in the GetIP function and is called in the FormCreate event.  As an alternative to this you may also use the function GetThePcsIPAddress that is located within AstaUtil.pas.  We call the SendSharedFileList procedure in the FormActivate.  Another good location to call this would be on the AstaClientSocket2CodedParamList afer the downloaded file has been saved.

Now that we have the Client communicating with the server we need to setup the Peer-to-Peer connectivity.  This is where the second TAstaClientSocket and the TastaServerSocket come into play.  Both are set to port 9010 and the properties are shown in figure 10 and 11 respectively.


Figure 10


Figure 11

The TAstaServerSocket will be used to listen for request and send files as requested from another client.  The OnCodedMessage event has been coded as follows:

procedure TfrmMain.AstaServerSocket1CodedMessage(Sender: TObject;
  ClientSocket: TCustomWinSocket; MsgID: Integer; Msg: String);

var
  tfs : TMemoryStream;
  apl : TAstaParamList;
  fs : String;

begin

  case MsgID of
     100 : begin
             apl := TAstaParamList.Create;
             tfs := TMemoryStream.Create;
             tfs.LoadFromFile(DirectoryListBox2.Directory+'\'+Msg);
             fs := StreamToString(tfs);
             tfs.Free;

             With apl do
               begin
                 FastAdd('FileName',Msg);
                 FastAdd('fs',fs);
               end;

             AstaServerSocket1.SendCodedParamList(ClientSocket,200,apl);
             apl.Free;
           end;

  end;

end;

A coded message with an MsgID of 100 is our flag that a user wants a file from us.  The file name is sent as the Msg.  To send the file back to the user we send an AstaParamList using the SendCodedParamList method with a MsgID of 200.  The AstaParamList contains 2 Parameters which are both strings.  The first is the File Name of the requested file.  The second is the actual file converted from a stream to a string using the StreamToString function located in AstaUtil.pas.

Next we code the OnDisconnect event with just a comment otherwise an exception is raised. Asta wants to verify that you know when a disconnect happens.

The second AstaClientSocket (AstaClientSocket2) is coded the opposite of the AstaServerSocket.

In the OnCodedParamList event we place the following code:

procedure TfrmMain.AstaClientSocket2CodedParamList(Sender: TObject;
  MsgID: Integer; Params: TAstaParamList);

var
  rfs : TMemoryStream;

begin

  case MsgID of
     200 : begin
             rfs := TMemoryStream.Create;
             StringToStream(Params[1].AsString,rfs);
             rfs.SaveToFile(DirectoryListBox1.Directory+'\'+Params[0].AsString);
             ShowMessage('Download Complete');
             AstaClientSocket2.Active := False;
           end;

     end;

end;

This code reads in the AstaParamList.  Params[0] is the File Name of the file and Params[1] is the File converted to a string.  Again we use another handy procedure from AstaUtil.pas called StringToStream to convert the data back into a stream.  Once we have the file loaded into our stream we can save it to the desired location using the SaveToFile method.

Just as we did on the AstaServerSocket we code the OnDisconnect event even with just a comment otherwise an exception is raised.

Now that we have the Peer-to-Peer connectivity ready to go we need a way to activate it.  This is coded in the Download button within the OnClick event as follows:

procedure TfrmMain.DownloadClick(Sender: TObject);

var
  i : integer;

begin 

//Connect and Request File
AstaClientSocket2.Active := False;

AstaClientSocket2.Address :=  AstaClientDataSet1.FieldByName('Host').AsString;

AstaClientSocket2.Active := True;

//Wait for connection
While not AstaClientSocket2.Active do

  Application.ProcessMessages;

//Send Request
AstaClientSocket2.SendCodedMessage(100,AstaClientDataSet1.FieldByName('FileName').AsString);

end;

When a user finds a file to download this code is executed.  AstaClientSocket2 is closed and set to the address of the Host with that particular file.  We then wait for the connection to be established.  Once connected we send a coded message with an ID of 100 and the File Name as the message.  The coding we just placed on the AstaServerSocket picks it up from there.

You’re now ready run app.  Happy Sharing!!!

Any comments or questions about this tutorial can be sent to cweston@astatech.com

 

ASTA in 60 Seconds | Products & Training | Developer Details | Free Internet Tools
Demos | Support & Manuals | Business Views | ASTA Vision | Tutorial Central
News | Contact Us | Testimonials | ASTA Links | Partners

Copyright © 2000, ASTA Technology Group.