Newsgroups : Borland : borland.public.delphi.internet.winsock : 2006 Jan : Re: TIdTCPConnection.Write freezes the current thread

www.cryer.info
Managed Newsgroup Archive

Re: TIdTCPConnection.Write freezes the current thread

Subject:Re: TIdTCPConnection.Write freezes the current thread
Posted by:"Remy Lebeau (TeamB)" (no.spam@no.spam.com)
Date:Thu, 5 Jan 2006 12:39:48

"Adam Lister" <adam@SPAMLESS.tombola.com> wrote in message
news:43bcf958$1@newsgroups.borland.com...

> I'm having some problems with an app that uses a TIdTCPServer
> component. (V9.0.17)

That is an old build.  The current snapshot build is 9.0.50.

> I *think* its something to do with a client connection dropping
> but the component/application thinks its still active.

The only way that can happen is if it is an is abnormal disconnect (hardware
failure, network failure, etc).  If the client intentially disconnected the
connection, then the server should be able to detect that immediately.

> The problem occurs at random intervals (up to 24 hrs apart) and
> as part of a "broadcast" where I write the same output string to
> each connection.

Are you using any timeouts?

> Originally I had the write command in the main thread, this resulted
> in the main thread freezing completely

As it should have, since Indy is a blocking library.

> no exceptions or errors are thrown. I switched it to a very inefficient
> approach where I create a new thread for every client and these
> threads do a single write command and then terminate.

Indy servers are already multi-threaded.  You do not need to create more
threads for them.  Especially since the approach you describe is prone to
race conditions that can corrupt the communications of a connection if you
don't provide thread-syncing when writing to the socket.

I would suggest writing a new class that derives from TIdPeerThread, and
have that class contain a thread-safe queue.  In the server's OnExecute
event, you can then check the queue for pending message for that connection.
This way, each connection maintains its own queue that is processed
independantly of any other connection.  For example:

    type
        TMyPeerThread = class(TIdPeerThread)
        private
            FQueue: TIdThreadSafeStringList;
        public
            constructor Create(ACreateSuspended: Boolean = True); override;
            destructor Destroy; override;
            procedure AddToQueue(const XML: String);
            property Queue: TIdThreadSafeStringList read FQueue;
        end;

    constructor TMyPeerThread.Create(ACreateSuspended: Boolean = True);
    begin
        FQueue := TIdThreadSafeStringList.Create;
        inherited Create(ACreateSuspended);
    end;

    destructor TMyPeerThread.Destroy;
    begin
        FreeAndNil(FQueue);
    end;

    procedure TMyPeerThread.AddToQueue(const XML: String);
    begin
        try
            FQueue.Add(XML);
        except
            on e: Exception do
                Form1.Log('AddToQueue() - Exception: ' + e.Message);
        end;
    end;

Then you can do the following in the form that owns the server:

    constructor TForm1.Create(AOwner: TComponent);
    begin
        inherited Create(AOwner);
        IdTCPServer1.ThreadClass := TMyPeerThread;
        //...
    end;

    procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
    begin
        TMyPeerThread(AThread).Queue.Clear;
    end;

    procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
    var
        LThread: TMyPeerThread;
        I: Integer;
    begin
        LThread := TMyPeerThread(AThread);
        with LThread.Queue.Lock do try
            For I := 0 to Count-1 do
                LThread.Connection.Write(Strings[I] + #0);
        finally
            Unlock;
        end;
        //...
    end;

    procedure TForm1.Broadcast(const XML: String);
    var
        I: Integer;
        RecClient: ...;
    begin
        with Clients.LockList do
        try
            for I := 0 to Count-1 do
            begin
                RecClient := Items[i];
                If RecClient.Auth then
                    TMyPeerThread(RecClient.Thread).AddToQueue(XML);
            end;
        finally
            Clients.UnlockList;
        end;
    end;


Gambit

Replies:

none

In response to:

www.cryer.info
Managed Newsgroup Archive