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 (16)


Creating High Performance Middleware Applications with IndyGo to Romeo Lefter's websiteFormat this article printer-friendly!Bookmark function is only available for registered users!
Indy Step by Step part 4
Product:
Delphi 4.x (or higher)
Category:
Multi-Tier
Skill Level:
Scoring:
Last Update:
03/17/2002
Search Keys:
delphi delphi3000 article borland vcl code-snippet Indy TCP/IP Middleware Socket client/server
Times Scored:
16
Visits:
15186
Uploader: Romeo Lefter
Company: Rombest Software
Reference: N/A
 
Question/Problem/Abstract:
Creating Middleware applications with indy
Answer:



Initially, this article was included in my Indy Step by Step series. But, because this technology is really cool I have decided to release it with another name.

Middleware is one of the coolest technologies that are now on the market. Unfortunately, ready to use tools for this technology costs much. This technology is associated with databases because it is intensly used in this field, but it is not limited with databases. Using this technology, you can create a plethora of applications thats use "thin" clients.
Tipically, a middleware framework looks like bellow:

[Clients] <<<-connection1->>> [Middleware server] <<<-connection2->>> [database Server]

In the image, the [Clients] representes your thin clients, the [Middleware server] is your application server and [Database Server] is the database. Conforming to this model, clients, the middleware server and the database server runs on different machines(the database server and midlleware server can runs on the same machine). Connection1 and Connection2 are the connections between our parts.
The middleware architecture is the best way to make serious economies. In example, the MS Sql server need suplimentar licence for each client. Also, it need something stupid think named "Client Access Licence". All of this costs money. With a single client licence (of course+Client Access Licence) you can build a middleware server and then work with many clients, without paying additional licences. And this solution works fine!

Related to middleware I want to say here that I don't like Midas, COM, DCOM, COM+ etc. There are many middleware solutions on the market. A little part of them represents cool solutions, but expensive. There are solutions that are easy to use but, unfortunately, slower.

The problem that I will discuss here is about how to create a free middleware application. This is possible and, belive me, superiour in performance then other competitors. First, let's review the tools we need for this job.


1. Indy http://www.nevrona.com/indy

You need indy. Because it is simple to use and very fast. There are another commercial packages on the market that are faster than indy, but working with them is not so easy. For me, indy is one of the best network packages on the market.

2. KbmMemTable: http://www.onelist.com/community/memtable

This is the best Memory Table that is now on the market. It is thread safe, it's content(records and structure) can be save on to disk, supports transactions, compressed blobs and much more. Best of all, it is free with source code.




TKbmMemTable and threads

   As I have said, the kbmMemTable threading model is one of the best. You have to set up just few properties and your table can safely work in a multithreading environment. Frirst property is AttachedMaxCount. It is a integer property and it stores the maximum no. of memtables that can be attached to this table. Attaching process is cool. When you attach a memory table (let's name it A) to another table (B) all the data contained in B table are avialable for the A table.  More than simply view, the A table can adds or updated records and the results are reflected in the B table. In a multi threaded environment, this is the best model.


It's time now to discuss about the protocol. First, for security reason, you need a authentification part. So, we have to implements 2 commands: Login and Logoff. The login command is sent to the server with 2 patameters: the username and the password, like bellow:

Login user pass

If the pair (user pass) coresponds to the data that are stored on the server, the user is able to work, else it is disconnected. When the user wants to terminate it's session, it sends to the server the Logoff command, without any parameter. In this moment, the server will disconnect the client.

In order to make this example simple and portable, I will work here with paradox tables, thats will emulate our database server. Also, for easy portability, I will use queries. So, in this application I will use the country.db that is in your DBDEMOS alias.
The clients that will connect to our middleware server will be able to:
- add records to this table
- get entire table

For adding records to table, we will implement an command (Add) with five parameters: Name, Capital, Continent, Area, Population. So, the command will look like bellow:
Add p1 p2 p3 p4 p5

For getting the table we need just a command without parameters (Get).

It's work time, so let's start. Open a new project, put a Memory table and a TIdTcpServer on it. The memory table will be used for the login process, so you have to create 2 string fields for it:user and password. You have to implement some procedures for adding, deleting and updating users. Also, on the FormCreate and FormClose events you have to load/save persistent data for this table(Using LoadFromBinaryFile/LoadFromCsvFile and SaveToBinaryFile/SaveToCsvFile methods). I have decided to use a memory table for handling user authentification because it is the best in speed and because it's thread-safe mechanism works perfectly. I have decided to have the following architecture:

On the MainForm:
AuthTable:-->A KbmMemTable used for authentification
Server:--> A TIdTcpServer component, our server
MThread:-->A TIdThreadMgrPool component (I'll use for this example a pool threaded with 100 pool size)


For easy understand, I'll use CommandHandlers Enabled in this projects, so we have to define 4 commands:
-Login
-Logoff
-Get
-Add



Because our pool size is 100, the AttachedMaxCount property of the AuthTable has to be 100. Also, for easy understanding we have to put all "database interface" components to a dataModule. So, create a datamodule and put sume components on it, like bellow:

Query1: --> A TQuery component thats will interface with the database, in our case database is DBDEMOS
Session1:--> A Tsession components used for safe transactions
logintable:--> A memTable thats will be attached to AuthTable in order to verify if the pair (user,password) is correct
buffertable:--> another memTable used for data exchange

Turn the AutoCreateForm to off for datamodule (in Project|Option).

Now we will look in depth on the server model. As I have said, the user has to login on the server. When the user send the LOGIN command the server will do the following steps:

1. Creates the datamodule;
2. Attach the logintable(from the datasource) to AuthTable(that is on the MainForm);
3. Verify if the pair (user,password) is valid;
4. Sends back to the user the authentification Result:
- If the pair (user, password) is valid, the server sends a message like ('OK, you are autentificated')
- If the pair (user, password) is not valid, the server sends a m,essage like ('Wrong user or password') and disconnect the user.

A little code will be more explicit if you haven't understand what I have said. So, look bellow:

procedure TForm1.serverCommandHandlers0Command(ASender: TIdCommand);
var
   ClientDataModule:TDatas;
   loginFlag:boolean;
begin
//If the client sends wrong no. of parameters, disconnect it!
if ASender.Params.Count<2 then
   begin
   ASender.Thread.Connection.WriteLn('Wrong no. of params! Good bye!');
   ASender.Thread.Connection.Disconnect;
   end;

//Create the dataModule.. It's owner is the actual Connection!
ClientDataModule:=TDatas.Create(ASender.Thread.Connection);

//Assign for the session component an unique name
ClientDataModule.Session1.SessionName:='ClientSession'+Inttostr(ASender.Thread.ThreadID);
ClientDataModule.logintable.AttachedTo:= AuthTable;

//Ok, now we will verify if the (user,password) is valid
if not ClientDataModule.logintable.Locate('User',ASender.Params[0],[]) then
   loginflag:=false
   else
   if ClientDataModule.logintable.FieldByName('Password').AsString= Asender.Params[1] then
    loginFlag:=true
    else
    loginFlag:=false;

if loginFlag then
   Asender.Thread.Connection.WriteLn('Ok, you are now in the system, man!')
   else
   begin
   Asender.Thread.Connection.WriteLn('Sorry, invald user or password. Good bye!');
   end;

end;

At first view, it is little "strange" for you the mode we have created the dataModule (in my project it's name is Datas). As you have observed, I have created it using a local variable. The problem is "how we will access it in other procedures". And here is my little inovation.  Using a global variable is really hard because we don't know how many connections we have in a moment. Don't forget that the owner of the DataModule is the Connection. For each active connection we have a DataModule created. Of course, it is created only if the user is logged on. If the user is not logged, the dataModule does not exists. For easy understanding look on the bellow scenario:
The client connects to the server, using the connect method of TIdTcpClient. In this moment, the client can sends any command to the server. We need to know that it is logged when sends to the server a command like Get or Add. Using the model I have described, the user verification is really easy, because if for client connection exists a datamodule, that meand that the client is logged, if not, the client is not logged. I think that I was clear in this explanation. If not, I'm sorry :).

It's time now to implement a "finder" function. This function is usefull for find if a datamodule exists for a connection. If the datamodule exists, the function will return a reference to the dataModule. Else, it will return nil.

function TForm1.FindModule(connection:TIdTCPServerConnection):TDatas;
var
   i:integer;
begin
Result:=nil;
for i:=0 to Connection.ComponentCount-1 do
   if Connection.Components[i] is TDatas then
    Result:=(Connection.Components[i] as TDatas);
end;

As I have said, we are using a "pool thread model" for our server. Thats means that the thread is not destroyed when the user disonnects from the server. So, we have to destroy manually the DataModule, each time when user id disconnected from our server:

procedure TForm1.serverDisconnect(AThread: TIdPeerThread);
var AData:TDatas;
begin
AData:=FindModule(AThread.Connection);
if AData<> nil then
   AData.Free;
end;

Thats means that for Logoff command we have to do something like bellow:

procedure TForm1.serverCommandHandlers1Command(ASender: TIdCommand);
begin
ASender.Thread.Connection.Disconnect;
end;

We are now in the moment of the real implementation. We have to exchange data between client and server, so we will implement first the Get command. In order to make this exaple easy tu understand this command will not have any parameters. But, you can add parameters thats can be used for filtering or for any other action. Bellow, you have the command implementation:

procedure TForm1.serverCommandHandlers2Command(ASender: TIdCommand);
var
   AData:TDatas;
   AStream:TStream;
begin
AData:=FindModule(ASender.Thread.Connection);
if Adata=nil then
   begin
   ASender.Thread.Connection.WriteLn('You are not logged in! Good bye!');
   Asender.Thread.Connection.Disconnect;
   end;

//Create the stream
AStream:=TMemoryStream.Create;

// Start the interogation
AData.Query1.sql.Clear;
AData.Query1.sql.Add('select * from country');
AData.Query1.Active:=true;

//Load query data into buffer table
AData.buffertable.LoadFromDataSet(Adata.Query1,[mtcpostructure]);

//Save bufferTable to stream
AData.buffertable.SaveToStream(AStream,[mtfSaveData, mtfSaveCalculated,mtfSaveDef,mtfSaveIndexDef,mtfSaveInLocalFormat]);

//Move the Stream to First
AStream.Seek(0,soFromBeginning);

//Send the stream to the client
ASender.Thread.Connection.WriteStream(AStream,true,true);

//Free the stream
AStream.Free;
end;

For this command, the client will receive a stream containing all table data. This stream can be loaded into a TKbmMemTable and the entire table on the server will be visible on the client. Without BDE or any other third party "connectivity".

Because the Add command is the sami in implementation (you have to read the params and create a query thats insert params into the table) I will not insist here with this aspect. Also, because the client implementation is really easy I will not insist on it.

This is the entire "technology". It is simple to implement, superior in performance and it costs nothing. There are, of course many other aspects you have to think about. In example you can use compressed streams in order to reduce the trafic.

I think this example was usefull for you and I am waiting your comments.Please, do not send comments directly to me using email! Use the delphi3000 comments, and I will respond.
Anyway after this series of tutorials about TCP and Indy, you can go to your boss to say that you are, starting from now, a TCP/IP expert.

Don't forget: Stay close to this tutorial. In the next part we will look in detail on the UDP protocol.





Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
Congratulations by Tutorial
    Rodrigo Medeiros (May 13 2008 9:46PM)

I would like to receive a demonstration by e-mail, it is possible?
Thanks for helping in the tutorial, it was excellent.

Rodrigo Medeiros
medeirosmaster@gmail.com
Respond

Demo
    Raimundo Carvalhedo (Dec 21 2005 11:18PM)

Please....demo server e client

thanks
Respond

Indy 10
    Matheus Pecci (Mar 1 2005 7:38PM)

Im using Indy 10 with delphi 2005 and in this version doesn´t exists the component TIdThreadMgrPool. How can I substitute it??

Thanks.
Respond

E-mail application
    Gustavo de Melo (Oct 31 2003 12:17PM)

Great job!

I'm loking for samples for how to create an application that could send and receive e-mails with attachments.

I'm gratefull for someone that know about this and sendme a very simple example.

Thanks!

Respond

Can you give me a ADO instance? Thanks...Because i use ADO in Delphi...
    namelysweet (Sep 15 2003 4:57AM)

Can you give me a ADO instance? Thanks...Because i use ADO in Delphi...your artical is really good!!!!


Can ADO write to Stream??????? i want to know...(for i am a beginner^_^)
Respond

RE: Can you give me a ADO instance? Thanks...Because i use ADO in Delphi...
Romeo Lefter (Oct 31 2003 4:22PM)

Working with ADO is the same. You can Load the data into kbmMemTable from any datasource, so it is almost the same.
Respond

very good aricle about indy to build app
    song ling (Jan 28 2003 2:17PM)

it's a very good article about indy that show how to build appliction with
different way that delphi provided .a new concept about multi-tie application bulding.
     but miss the attachment demo project,how can i view the demo project of this article .thx
   owen
email: fhaixiong@163.net
Respond

Good job!
    Liutec Andrei (Sep 22 2002 6:04PM)

It's a great thing to share with the world part of the "romainan" inventivity. Simple (and free) solutions are always the best.

Thanks!
Respond

How to integrate?
    Klaus Edelmann (Sep 20 2002 4:17PM)

Hi!  

I'm really interested in this argument. So I tried out to build the app after yopur advise. But I can't understand how to integrate the procedures ServerCommandhandlers?Command since the TIdTcpServer doesn't have a an event where the needed parameter ASender: TIdCommand is findable.
Could you help me in this case?

Thanx in advance!!

Klaus
Respond

Nice work.
    anonymus (Apr 18 2002 11:50PM)

FWIW, MS SQL (and probably all the others) always considered 10 clients connected to 1 middleware server to be 10 clients and required 10 licenses.
Respond

Middleware Development
    Norman McFarlane (Mar 29 2002 6:01PM)

Great article. It is worth looking at the work that Francois Piete has done in this field. He has a mature set of components ready for use that bypass COM, DCOM, COM+ etc.
Respond

Test
    TT (Mar 21 2002 7:11PM)

Sorry, but it's only a test!
Respond

Dont Like Midas?
    César Nicolás Peña Núñez (Mar 17 2002 8:34PM)

I am a newbie, I started programming n-tier apps 6 or 7 months ago and I have found Midas a great tool to shield you from COM, DCOM, OLE, etc. No tool is perfect while I reckon that the midas architecture has its faults (based 100% on COM) I do think that for the newbie Midas is a excellent tool.

Believe it or not COM, DCOM, COM+ lifespan will be bigger than we all might want, but then again Delphi allows us to build good alternatives to Midas.

BTW we all should poll our knowledge to make a good midas alternative (HTTP, TCP/IP have their share of advantages and disadvantages  some of wich are shown in the very midas)
Respond

Thak you
    Khaled Shagrouni (Mar 17 2002 1:03PM)

You said above : 'I don't like Midas, COM, DCOM, COM+ etc.'

Totally agreed, Why I should leave a technology (TCP/IP) that I am sure will be valid up to the next 10 years without any risk of change.

Thank you for the article :)

Khaled.
Respond

Inserts & Updates?
Ajay (Mar 18 2002 5:11AM)

HI,
Now tha we know how to retrieve records, how about doing inserts and updates on tables.
Regards
Ajay Sharma
Respond

RE: Inserts & Updates?
Romeo Lefter (Mar 18 2002 5:46AM)

This is the same...Anyway, in short time I'll attach a complete example with client and server
Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
R. Lefter
 
   














 







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