Newsgroups : Borland : borland.public.delphi.internet.winsock : 2006 Jan : 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
none