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
Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.


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....

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

Networking for the Cloud Era

Join Microsoft and Riverbed for a discussion and demonstration of enhancements to SteelConnect:
-One-click orchestration and cloud connectivity in Azure environments
-Tight integration of SD-WAN and WAN optimization capabilities
-Scalability and resiliency equal to a data center

Question has a verified solution.

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

Suggested Solutions

Title # Comments Views Activity
Delphi 2 69
Is anyone willing to have a look through this code and help debug? 25 84
CheckListBox usage 3 71
How to make Sign in, using Clientdataset? 1 28
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Two types of users will appreciate AOMEI Backupper Pro: 1 - Those with PCIe drives (and haven't found cloning software that works on them). 2 - Those who want a fast clone of their boot drive (no re-boots needed) and it can clone your drive wh…
In a recent question ( here at Experts Exchange, a member asked how to run an AutoHotkey script (.AHK) directly from Notepad++ (aka NPP). This video…

828 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