Indy TIdAntiFreeze not working?

Posted on 2004-08-17
Last Modified: 2011-10-03
I have an existing client server project that I am converting to use TIdTCPClient and TIdTCPServer.  The "old" version used TClientSocket & TServerSocket with non-blocking sockets.  

The current problem is with the Server.  I have a TIdTCPServer and TIdAntiFreeze components on the main server form.  The Execute method is very simple (right now):

procedure TfmMain.IdTCPServer1Execute(AThread: TIdPeerThread);
  Client: PClientInfo;
  sTaskType: string;
  if (AThread.Terminated) or (not AThread.Connection.Connected) then EXIT;
  Client := PClientInfo(AThread.Data);
  Client.LastAction := Now;
  TaskMsgStrList.CommaText := AThread.Connection.ReadLn;  // <== input to fmMain.TaskMsgStrList
  AThread.Connection.WriteLn(TaskMsgStrList.CommaText);   // <== just ECHO it
  sTaskType := LowerCase(TaskMsgStrList.Values['TaskType']);
  if sTaskType = 'login' then
    Client.UserName := TaskMsgStrList.Values['UserName'];

The problem is that in between communications the Server form is completely unresponsive.  It appears to be "blocked" on the ReadLn statement above.

I thought that that was what the TIdAntiFreeze component was supposed to prevent.
Question by:PHenningsen
  • 5
  • 3

Expert Comment

ID: 11827500
Antifreeze is for client use only.

Each Execute is running in the context of AThread, so it does not need antifreeze. Antifreeze basically calls application.processmessages on the client side so that if you use a tcpclient in the mainVCL it will not prevent you cancelling things.

Generally if your code is sitting tight on the readln then it has not received an end of line character in the data.
you may find that the antifreeze is having an adverse reaction in the server code.

Expert Comment

ID: 11827505
also. remember you are in a thread context.

 TaskMsgStrList.CommaText := AThread.Connection.ReadLn;  // <== input to fmMain.TaskMsgStrList

This should be in a syncronize if it is talking to Visual VCL code like a memo etc etc.

Author Comment

ID: 11835864
In all my recent reading about Indy, I never once read that "Antifreeze is for client use only".  But it makes sense.  Anyhow removing it had no effect.

What do you mean by "it has not received an end of line character in the data"?  So far, I'm just using ReadLn & WriteLn.  And I find no place to specify an EOL character.

Further, after the Client sends a msg, the Server responds to my mouse click, but then the Server is still "fubar"...  To the extent that I have to use Task Manager to kill Delphi (yes, Delphi, not the app it was debugging) before continuing.

Re: TaskMsgStrList.CommaText := AThread.Connection.ReadLn.  TaskMsgStrList is intended to be part of "thread local storage".  So my next question is: How do I extend TIdPeerThread to add it?  Where might I find an example of doing that?

Phil Henningsen

Author Comment

ID: 11836071
Further on the Server being "unresponsive":  The Server has a TPageControl with 5 Pages or Tabs.  If I start the Server, and click on each tab BEFORE starting the Client, then all is well.

If I start the Server, then start the Client, then the Server goes unresponsive after the 1st transaction is processed.

Very wierd....
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.


Accepted Solution

KyleyHarris earned 500 total points
ID: 11836198
It is not weird at all. It is the reason that threads have the synchronize() method for talking to VCL code.
Each gui control has a property Handle:THandle, which binds it to the operating system to receive messages. To save resource. The handle property is only allocated on first use.

ie Memo1.Lines.Add('test')

if this is the first time you've used the memo then that is when the handle is created. if you access a VCL control from within a thread not using synchronize then the Handle is actually created using the thread process as the owner instead of the VCL. from then on your program just won't work properly.

You need to double buffer your server threads using critical sections and intermediary data.

Binding Session Objects.

Not really sure what PClientInfo is?
Typically in the OnConnect event of the server I would create a session object (userdefine) which contains all resources needed by a client session to do its job and
bind it to the thread with the thread Data property. ( I normally go the other way and have a threadsafe list of objects and do the lookup based on threadid)

ie  (sample only, not real)
TUserSession = class(TObject)
  property Thread:TIDPeerThread;
  property UserName:string;
  property Datamodule:string;
  property Privelages:TUserPrivelages

  property ClientInfo:PCLientInfo; // adding your stuff
  property TaskMsgList:TSTringList;
everything this object should be thread safe, and should not talk to the VCL.

in on execute find this object and make use of it.

in the OnDisconnect you free this object.

typically I dont build visual servers. (I have been using indy for 4 years for server based controls)  I build non-visual servers and write information to logs. if i want to see what is happening on the server then I pump out statistics to another client viewer on regular intervals or via messaging systems.

i think the main key here with your issue is the binding of Threaded code into the main VCL. its just a no- no.


Expert Comment

ID: 11836273
One other note on ReadLn and WriteLn.

by using this method of retrieval you are opening your system up to buffer overrun attacks from hackers.

WriteLn('Hi') sends 'Hi'#13#10 over the socket

Readln will get 'Hi'#13#10 and finish the job returning 'Hi' as the result.

if someone conects to your server and does something like

while true do write('a');

Readln will never be able to complete its readln and your server will eventually run out of memory. This is a common past time of people who like to ruin software.
I tend to always use know size parameters.


to send a string 'Hello'
I do something like this


on the other side

ASize := ReadInteger;
if ASize < 1000000 (or some time of validation ) then
  AString := ReadString(ASize);

this is also good from a debugging point of view and results in a faster processing server because it does not have to calculate the length of the data. Essentially it is a PASCAL style string with the size byte first so that you don't need a delimiter at the end.

Hope this helps


Author Comment

ID: 11836636
Thanks so much.  The light is beginning to dawn.

Since the Server's OnConnect procedure was:
procedure TfmMain.IdTCPServer1Connect(AThread: TIdPeerThread);
I pretty much assumed that I was operating in the context of TfmMain.  The culprit was (in ..Connect)
  AThread.Synchronize(RefreshClientListDisplay);  // ie the lack of "synchronize"

Could you expand a bit on what you meant by:
"You need to double buffer your server threads using critical sections and intermediary data."

And, do you mind if I leave this question open for a day before I award you all 500 points & an "A"?

Expert Comment

ID: 11836904
I dont mind about the points at all.

By double buffering I mean... don't talk to the GUI. Make your server non-visual (Dump the controls on a datamodule) Have it so that the threaded server portion adds information to a non-visual control. Create yourself a TCriticalSection (unit SyncObjs) and wrap this around any code which is shared among threads. Then instead of telling the GUI to refresh itself directly, have the GUI examine the server for properties. ie ClientList property, and update itself accordingly. You could use a ttimer or something to keep it simple. The mecansim I uses is thread safe Observation, using a Signal & Slot pattern (To hard to explain here).

I usually think that code should be nicely de-coupled. Make the GUI look at the non-gui, this way it is easy to split the non-gui into a service object.

fuzzy logic code ... :)

FMyTCPServerDM = datamodule with TidTCPServer and all other stuff. with a critical section etc etc.

FMyTCPServerDM.Lock =
  FCS.Acquire; (Local owned critical section)

FMyTCPServerDM.UnLock =
  FCS.Release; (Local owned critical section)


  if FLastClientListUpdateTime <> FMyTCPServerDM.FLastClientUpdateTime then
       Memo1.Lines.Assign(FMyTCPServerDM.ClientList) // Clientlist is a locally owned TStringList;
    FLastClientUpdateTime := FMyTCPServerDM.FLastClientUpdateTime;

//    set FMyTCPServerDM.FLastClientUpdateTime := now; in OnConnect and OnDisconnect;
//    update FMyTCPServerDM..ClientList in OnConnect and OnDisconnect;



Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Convert GUI app into console app for Win32 Env 5 81
Convert a string into a TDateTime 5 50
Base1 Encode/Decode 3 68
How to build JSON File in Delphi 6 3 13
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Windows 10 is mostly good. However the one thing that annoys me is how many clicks you have to do to dial a VPN connection. You have to go to settings from the start menu, (2 clicks), Network and Internet (1 click), Click VPN (another click) then fi…
Internet Business Fax to Email Made Easy - With  eFax Corporate (, you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, f…

920 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now