Visit our Sponsor   Visit our Sponsor
delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
500 Users Online NOW
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi



Loremo - the 1.5 liter car coming in 2009




Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (6)


How to write a TCP Redirector using IndyFormat this article printer-friendly!Bookmark function is only available for registered users!
TCP Redirection Server, with client connection
Product:
Delphi 5.x (or higher)
Category:
Communication
Skill Level:
Scoring:
Last Update:
08/04/2004
Search Keys:
delphi delphi3000 article borland vcl code-snippet TCP Redirect Server Client Port Mapping Indy
Times Scored:
9
Visits:
11198
Uploader: Kim Sandell
Company: Celarius Oy
Reference: N/A
 
Question/Problem/Abstract:
Many people ask how to write servers in Indy, this primer goes through how a TCP server is created, and how to redirect all traffic to another remote server. This is the same as port-mapping in firewalls.
Answer:



TCP Protocol Redirection Primer
===============================

Contents

1    Introduction
1.1  Disclaimer

2    Writing the Server
2.1  Writing the servers OnExecute() method
2.2  Writing the client connection
2.3  Forwarding data to/from the server/client
2.4  Testing the application

3    Where to next ?

--------------------------------------------------------------------
1    Introduction
--------------------------------------------------------------------
  This article explains how to create a TCP protocol redirecter with
  Delphi 5 or Delphi 6, using the Indy TCP components.

  The purpose of this is to show how to accept a real client
  connection to an IdTCPServer, and then how to further connect to
  another remote server, and forward all data coming/going to/from
  the client/server.

  In the article well use the resulting application to redirect
  traffic to the delphi3000.com www site, to port 80. The local port
  will also be 80, so in effect all traffic coming in on port 80 will
  be forwarded to delphi3000.com.


--------------------------------------------------------------------
1.1  Disclaimer
--------------------------------------------------------------------
  This article is written by Kim Sandell, September 2002. You can
  reach me via email at "sandell@celarius.com" if you have
  comments or corrections to the material.

  This material is free. It may be used in any way the reader
  sees fit, but at the readers own risk.


--------------------------------------------------------------------
2    Writing the Server
--------------------------------------------------------------------
  In order to write the server, we need to have a new
  application in delphi, the IdTCPServer component dropped
  onto the form, and two buttons, one Button1 as a Start
  button and another Button2 as a Stop button.

  The first thing we need to define is the port the IdTCPServer
  component will listen to. This is done by setting the DefaultPort
  property of the IcTCPServer1 to 80. (80=www port remember!)

  We also need to define the basic functions for the buttons,
  and create an event handler for incoming client connections.

  For the Button1, create an OnClick event handler:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
       IdTCPServer1.Active := True;
    end;

  and for the Button2, create an OnClick event handler:

    procedure TForm1.Button2Click(Sender: TObject);
    begin
         IdTCPServer1.Active := False;
    end;

  Go to the properties of the Events of the IdTCPServer and
  create an event handler for the OnExecute event.

    procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
    begin

    end;

  
--------------------------------------------------------------------
2.1  Writing the servers OnExecute() method
--------------------------------------------------------------------
  The OnExecute() event is the place where the client connects to.
  The parameter "AThread" that is passed with it, is the actial
  thread with all the information about the connection.

  In this demo we will use only a few of these, but when you get the
  hang of all of this, you can start experimenting a bit with the
  AThread object.

  First we need to make sure that everything that happends in the
  handler is thread-safe. This means we can not update any visual
  properties, nor can we use any global variables/objects.

  We also want to make sure that if anything goes wrong we can handle
  the situation and cleanup after the client.

  We will begin by making a "Try Except End" plus a "Try Finally End"
  statement wrapped inside each other.

    procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
    begin
         Try
            Try
            Finally
            End;
         Except
         End;
    End;

  Everything that we do in the OnExecute() method must go inside the
  try except statement, and most of it will go in the try finally
  statement.


--------------------------------------------------------------------
2.2  Writing the client connection
--------------------------------------------------------------------
  To make a TCP Client connection to another server/machine we need
  the IdTCPClient component. We have to make a local variable so that
  we are thread-safe (see above). This component is found in the
  IdTCPClient pas file, so we need to include that in the Uses clause
  in the "implementation" section:

    .....
    var
      Form1: TForm1;

    implementation

    Uses IdTCPClient;  // This line includes the correct component

    {$R *.dfm}
    ....

  The TCPClient variable well use is called CLI. This has to be
  declared in the OnExecute() method locally. Also we need to create
  the component, and make sure it is destroyed once the connection
  is terminated.

  We also want to tell it where to connect to once a client connects
  to the server, so well set the Host and Port properties of it.

  In the finallt statement we want to make sure the real client
  that connected is also disconnected, so well throw in a Disconnect
  for the AThread.Connection as well.


    procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
    var
      Cli : TIdTCPClient;
    begin
         Try
            Cli := NIL;
            Try
               { Create & Connect to Server }
               Cli := TIdTCPClient.Create( NIL );
               Cli.Host := 'www.delphi3000.com';
               Cli.Port := 80;
            Finally
               If Assigned(Cli) then
               Begin
                    Cli.Disconnect;
                    Cli.Free;
               End;
               { Disconnect real client }
               AThread.Connection.Disconnect;
            End;
         Except
         End;
    End;

  Now when a client connects to us, we need to establish a connection
  to the remote server, so we immediately try to connect to that
  server using the Cli.Connect; method.

            ...
            Try
               { Create & Connect to Server }
               Cli := TIdTCPClient.Create( NIL );
               Cli.Host := 'www.delphi3000.com';
               Cli.Port := 80;
               { Connect to the remote server }
               Cli.Connect;
            Finally
            ...

  If we get an error (exception) here, the code will jump to the
  finally statement, where the CLI component is freed, and the
  connection to the Client is disconnected immediately. In effect
  the only thing the real client sees is that a connection was made
  but the connection was immediately lost before any data came
  through.


--------------------------------------------------------------------
2.3  Forwarding data to/from the server/client
--------------------------------------------------------------------
  Now that we have the real client connected, and we also have a
  connection to the remote server, we want to start forwarding data
  to/from the client/server.

  This will be done for as long as both parties are connected to
  us, so well need a loop that keeps checking if data is coming or
  going.

  In the loop well check if the real client has any data to send, and
  also check if the remote server as any data to send to the client.

  We also need to check for disconnection on both ends, since the
  Indy components do not alwyas notify of disconnections.

  Last but not least: We need to make sure the loop does not take
  100% CPU time, since it is going pretty fast through the loop.
  To avoid that we will add a Sleep command at the end of the loop.

  The complete OnExecute() handler should now look like this:

    procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
    Var
       Cli        : TIdTCPClient;
       Len        : Cardinal;
       Data       : String;
    begin
         Try
            Cli := NIL;
            Try
               { Create & Connect to Server }
               Cli := TIdTCPClient.Create( NIL );
               Cli.Host := 'www.delphi3000.com';
               Cli.Port := 80;
               { Connect to the remote server }
               Cli.Connect;
               { Read/Write loop }
               Repeat
                  { Read data from Client }
                  { Uncomment the line below, depending on your
                    INDY version }
                  // <9: If AThread.Connection.CurrentReadBufferSize>0 then
                  // >9: If Lenght(AThread.Connection.CurrentReadBuffer)>0 then
                  Begin
                       Len := AThread.Connection.CurrentReadBufferSize;
                       Data := AThread.Connection.ReadString(Len);
                       { Write it to the Server }
                       Cli.Write( Data );
                  End;
                  { Read data from Server }
                  If Cli.CurrentReadBufferSize>0 then
                  Begin
                       Len := Cli.CurrentReadBufferSize;
                       Data := Cli.ReadString(Len);
                       { Write it to the Server }
                       AThread.Connection.Write( Data );
                  End;
                  { Check for Disconnects }
                  Cli.CheckForDisconnect(False);
                  Cli.CheckForGracefulDisconnect(False);
                  AThread.Connection.CheckForDisconnect(False);
                  AThread.Connection.CheckForGracefulDisconnect(False);
                  { Release system slizes }
                  SleepEx(1,True);
               Until (NOT AThread.Connection.Connected) OR (NOT Cli.Connected);
            Finally
               If Assigned(Cli) then
               Begin
                    Cli.Disconnect;
                    Cli.Free
               End;
               { Disconnect real client }
               AThread.Connection.Disconnect;
            End;
         Except End;
    end;

--------------------------------------------------------------------
2.4  Testing the application
--------------------------------------------------------------------

  Compile and run the application.

  Try connecting with your browser to the "http://localhost" address.

  You should see the delphi3000.com website !!!

  Note: This does not work if you must have a proxy defined in your
        browser !!

--------------------------------------------------------------------
3    Where to next ?
--------------------------------------------------------------------
  Now that you have learned the basics of redirecting TCP
  connections, I think the next logial step would be to expand the
  application with more features.

  A few suggestions for small enhancements:

  - Max number of connected users at one time

  - A timeout disconnect if nothing happends (no data transferred).

  - Recognise protocols, such as HTTP and actually block unwanted
    url's from beeing accessed. This would almost be a proxy, even
    thoug a proxy needs to check where the client wants to go first.

  Some of the suggestions are propably easy to implement, others
  harder. The level of expertiese the programmer (you) has really
  is the only limit here, and if you want to learn something new,
  then learning by doing is the best way.

  Enjoy the article, and have a nice day.





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
all ports
    terramoto terramoto (Dec 27 2005 3:42PM)

how do i do if i want to get all my ports redirected? thanks X)





--- Portuguese Online ---
Respond

RE: all ports
Kim Sandell (Dec 30 2005 2:05PM)

Hi, It's going to take a little more than the code presented to redirect all the ports. The code here allocates resources for each redirection/connection so it's not going to be feasible to redirect all ports using the component.

What you propably want to dig into is how to override the default tcp/udp stack in windows, much the same way firewalls do. What you'd be doing is a very light-weight firewall with port-mapping capabilities.

Cheers,
Kim
Respond

Another way to accomplish the same thing
    Alan Fletcher (Sep 26 2002 3:55AM)

Here is another way of accomplishing the same thing:

Drop a TIdMappedPortTCP from the Indy Servers tab.
Set the MappedHost and MappedPort properties to the server you want the traffic to be redirected to;
Set the DefaultPort property to the port you want to listen for new client connections.
Set Active to True.
Compile and run.

Presto! Same thing accomplished w/o a single line of code. This component handles all the work for you. However, one can handle some of the events such as connects, disconnects and so on.

Alan
Respond

RE: Another way to accomplish the same thing
Kim Sandell (Sep 26 2002 9:02AM)

You are very correct in what you are saying. The purpose of the article was to show how it is done, so putting a mapped port on the form and setting a few properties would not have made an article.

But you are right, if one is going to do exactly what the articel describes then a mapped port is waht one should use !

Thankyou for pointing this out to me, It seems I forgot to include anything about the mapped port components in the article itself.
Respond

RE: RE: Another way to accomplish the same thing
Alan Fletcher (Sep 26 2002 8:17PM)

I agree with you that your article has a didactic value. However, like you conclude you should have mentioned that a component to do mapping already exists.

Alan
Respond

RE: RE: RE: Another way to accomplish the same thing
Charles Brown (Oct 28 2002 12:11AM)

Thank you for a very informative article. Often knowing how something actually works is far more useful than simply knowing how to do it. Such articles and the insights they provide are the very foundation of my own work, such as it is, and I take my hat off to you and all the others wiling to share their skills.

kind regards, Charles
Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
M. Kleiner
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)