Delphi-Kylix
  Home arrow Delphi-Kylix arrow Page 3 - A Real World Client Server Application in ...
Dev Articles Forums 
ADO.NET  
Apache  
ASP  
ASP.NET  
C#  
C++  
ColdFusion  
COM/COM+  
Delphi-Kylix  
Design Usability  
Development Cycles  
DHTML  
Embedded Tools  
Flash  
Graphic Design  
HTML  
IIS  
Interviews  
Java  
JavaScript  
MySQL  
Oracle  
Photoshop  
PHP  
Reviews  
Ruby-on-Rails  
SQL  
SQL Server  
Style Sheets  
VB.Net  
Visual Basic  
Web Authoring  
Web Services  
Web Standards  
XML  
Mobile Linux 
App Generation ROI 
IBM® developerWorks 
Sun Developer Network 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
DELPHI-KYLIX

A Real World Client Server Application in Delphi
By: Leidago
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 5 stars5 stars5 stars5 stars5 stars / 10
    2006-12-26

    Table of Contents:
  • A Real World Client Server Application in Delphi
  • The Client
  • The Server
  • The Complete Server Code

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    A Real World Client Server Application in Delphi - The Server


    (Page 3 of 4 )

    On the server side of things, we are not going to use the server application that we created earlier. Instead we are going to create a new  application that will use a different TCP/IP component. I want a new application because I want us to use the OnExecute method to power the server. You will recall from previous articles that there are two methods of programming an indy server, Command Handlers and OnExecute methods.

    In our previous example we saw how to create and implement  a server application using the Command Handler method. So create a new  application and drop a idTCPServer component from the Indy Servers tab. Also drop a memo component.

    First, we need to write a class that will collect client information as they connect and use that information when writing to the client:

    public

                IP: String;

                cname: String;

                procedure SendResponse(const Clientname: String;
    const AResponse: String);

            end;

    This gives us two levels of differentiating between clients, the IP address and the client's nickname. So, if you are running a chat or IRC server, and need to ban a user, you will have two avenues in which to do it.

    Next, we need to use another class to write thread safe messages. We will use the TWriteResponse class that we created earlier in the client section of the article. You already know what that class does, so I will not be explaining it here.

    The next thing we need to do is make the methods of the context class available to the ClientInfo class that we created earlier. This makes it possible to (for example) add all connected clients to a context list, among other things.  So add this line to the TForm1 class definition:

    constructor Create(AOwner: TComponent);override;

    Then in the implementation section of the form add the following:

    constructor TForm1.Create(AOwner: TComponent);

        begin

            inherited Create(AOwner);

            idTCPServer1.ContextClass := TClientinfo;

        end;

    This code basically transfers all the procedures and methods of the Context class to our newly created ClientInfo class. Next, we need to fill the two variables contained within the ClientInfo class. This is done when the client connects. So click on the OnConnect event of the idtcpserver component and add the following code:

    procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);

    begin

    with TClientinfo(AContext) do

            begin

                if (Connection.Socket <> nil) then

                IP :=Connection.Socket.Binding.PeerIP;

                cname := Connection.IOHandler.ReadLn;

                if cname <> ''then

                begin

                connection.IOHandler.WriteLn('Welcome '+ cname);

                end

                else

                //Client did not send a name...

                begin           

                connection.IOHandler.WriteLn('You did not send a
    name. Please send a name next time you try to connect!');

                connection.Disconnect;

                end;

    end;        end;

    All that happens here is that the server takes the client's IP address:

                IP :=Connection.Socket.Binding.PeerIP;

    and the client's name:

                cname := Connection.IOHandler.ReadLn;

    If the client does not send a name, it sends a message to the client and disconnects it:

                if cname <> ''then

                begin

                connection.IOHandler.WriteLn('Welcome '+ cname);

                end

                else

                //Client did not send a name...

                begin           

                connection.IOHandler.WriteLn('You did not send a
    name. Please send a name next time you try to connect!');

                connection.Disconnect;

                end;

    Now let's get down to the meat of the server program. We are still using the custom protocol that we discussed in the previous article. The protocol contains three commands, Aquote, Adate and Quit. So in order for the client to get a response from the server it will need to send one of these commands. We are going to set the OnExecute method to handle the client request. So double click on the OnExecute event of the server component and add the following code:

    procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);

    var

    thedate,request,cmd,response,AFormat,sentfrom:string;

    i,j:integer;

    arr:Array[1..6] of String;

    begin

    A

    //the request from the client is received in the request var

    request:=acontext.Connection.IOHandler.ReadLn;

    B

    // we need to break the request up into command(cmd) and sender
    (sentfrom)

    i:=pos('@',request);

    j:=pos(';',request);

    cmd:= Copy(request,1,i-1);

    sentfrom := Copy(request,i+1,j-i-1);

    C

      //Check which command the client sent

     if cmd='aquote' then begin

     Randomize;

      arr[1]:='I still miss my ex, but my aim is improving';

      arr[2]:='Last night I was looking up at the stars and I was
    wondering, where the heck is my ceiling?';

      arr[3]:='Do Roman paramedics refer to IVs as fours?';

      arr[4]:='I can resist everything except temptation.';

      arr[5]:='There is one thing I would break up over and that is
    if she caught me with another woman. I wouldn''t stand for
    that.';

      arr[6]:='I''ve often thought that the process of aging could be
    slowed down if it had to go through Congress.';

      i:=1+ Random(6);

     //Send the response to the client...

    TClientinfo(AContext).SendResponse(sentfrom,arr[i]);

        End

    D

        else

        if cmd='adate' then begin

        AFormat := 'yyyy-mm-dd hh:nn:ss';

        thedate:=FormatDateTime(AFormat, Now);

        TClientinfo(AContext).SendResponse(sentfrom,thedate);

        End

    E

        else

        if cmd='quit' then begin

        TClientinfo(AContext).Connection.Disconnect;

        end

        else

    F

        begin

        TClientinfo(AContext).SendResponse(sentfrom,'Unknown
    Command');

        end;

    end;

    I've divided the code up into alphabetical sections for easy reference. Part A basically receives the request from the client. The request comes in the form

    cmd@fromname;

    So for example a client requesting a quote will sent a request like so:

    aquote@joeblogg;

    We need the name of the sender because we will sent the quote back to him or her at some stage. Section B is responsible for breaking up that request into command and sender's name. It does that by using Delphi's pos() and copy() functions:

    i:=pos('@',request);

    j:=pos(';',request);

    cmd:= Copy(request,1,i-1);

    sentfrom := Copy(request,i+1,j-i-1);

    Now we have a command and a sender's name in separate variables. Now all we need to do is check which command the client sent and then respond to the client using the SendResponse() procedure. This is what sections C through E will do:

      //Check which command the client sent

     if cmd='aquote' then begin

     Randomize;

      arr[1]:='I still miss my ex, but my aim is improving';

      arr[2]:='Last night I was looking up at the stars and I was
    wondering, where the heck is my ceiling?';

    <snip>

     //Send the response to the client...

    TClientinfo(AContext).SendResponse(sentfrom,arr[i]);

    End

        if cmd='adate' then begin

        AFormat := 'yyyy-mm-dd hh:nn:ss';

        thedate:=FormatDateTime(AFormat, Now);

        TClientinfo(AContext).SendResponse(sentfrom,thedate);

        <snip>

        else

        if cmd='quit' then begin

        TClientinfo(AContext).Connection.Disconnect;

        end

    When a client sends a command that is not part of our protocol it gets the following response as written in section F:

        begin

        TClientinfo(AContext).SendResponse(sentfrom,'Unknown
    Command');

        end;

    Throughout the response you must have noticed the

    TClientinfo(AContext).SendResponse();

    The SendResponse procedure has the following code:

    procedure TClientInfo.SendResponse(const Clientname: String;
    const AResponse: String);

        var

            List: TList;

            Context: TClientInfo;

            I: Integer;

        begin

           // FContextList is inherited from TIdContext

            List := FContextList.LockList;

            try

                for I := 0 to List.Count-1 do

                begin

                    Context := TClientInfo(List[I]);

                    if Context.cname = clientname then

                    begin

                        try

            Context.Connection.IOHandler.WriteLn(AResponse);

      except

                        end;

                        Exit;

                    end;

                end;

            finally

                FContextList.UnlockList;

            end;

            Self.Connection.IOHandler.WriteLn('this server cannot
    find the client you sent the message to.');

        end;

    All that this procedure does is compare the client name with the names on the internal list of the ClientInfo class. The list is contained by the Context class whose methods we have transferred to our ClientInfo class. So in effect, the ClientInfo object is now maintaining it. So all we need to do is search for the client name  on the list, and if we find it, we send the response to the client  

    List := FContextList.LockList;

            try

                for I := 0 to List.Count-1 do

                begin

                    Context := TClientInfo(List[I]);

                    if Context.cname = clientname then

                    begin

                        try

            Context.Connection.IOHandler.WriteLn(AResponse);

      except

    otherwise we send an error message back to the client:

    Self.Connection.IOHandler.WriteLn('this server cannot find the
    client you sent the message to.');

    And that is all it takes to write a fully functional, and more importantly thread safe, client server application.

    More Delphi-Kylix Articles
    More By Leidago


       · Client Server applications are used in almost all spheres of IT, and is very much a...
       · Leidago, Thanks for this tutorial, it works great and I have already implemented in...
     

    DELPHI-KYLIX ARTICLES

    - Delphi Wrapper Classes and XML
    - Delphi and the DOM
    - Delphi and XML
    - Internet Access: Client Service
    - Finishing the Client for an Internet Access ...
    - The Client for an Internet Access Control Ap...
    - User Management for an Internet Access Contr...
    - Important Procedures for an Internet Access ...
    - Server Code for an Internet Access Control A...
    - Constructing the Interface for an Internet A...
    - Building a Server Application for an Interne...
    - Building an Internet Access Control Applicat...
    - Client Dataset: Working with Data Packets an...
    - Using the Client Dataset in an N-Tiered Appl...
    - Using the Client Dataset in Two-Tiered Clien...






    © 2003-2008 by Developer Shed. All rights reserved. DS Cluster 4 hosted by Hostway
    Stay green...Green IT