I want to make a TCPserver and send/receive message to clients as needed, not OnExecute event of the TCPserver.
Send/receive message is not a problem; I do like that:
procedure TFormMain.SendMessage(IP, Msg: string);
var
I: Integer;
begin
with TCPServer.Contexts.LockList do
try
for I := 0 to Count-1 do
if TIdContext(Items[I]).Connection.Socket.Binding.PeerIP = IP then
begin
TIdContext(Items[I]).Connection.IOHandler.WriteBuffer(Msg[1], Length(Msg));
// and/or Read
Break;
end;
finally
TCPServer.Contexts.UnlockList;
end;
end;
Note 1: If I don't use OnExecute, the program raise an exception when a client connects.
Note 2: If I use OnExecute without doing anything, the CPU usage goes to %100
Note 3: I don't have a chance to change the TCP clients.
So what should I do?
Use OnExecute and if you have nothing to do, Sleep() for a period of time, say 10 milliseconds. Each connection has its own OnExecute handler so this will only affect each individual connection.
The
Indy
component set is designed to emulate blocking operation on a network connection. You're supposed to encapsulate all your code in theOnExecute
event handler. That's supposed to be easier, because most protocols are blocking any way (send command, wait for response, etc).You apparently don't like it's mode of operation, you'd like something that works without blocking. You should consider using a component suite that's designed for the way you intend to use it: give the ICS suite a try! ICS doesn't use threads, all the work is done in event handlers.
I had similar situation taking 100% CPU and it solved by adding IdThreadComponent and:
Is it right? I am not sure.
In the OnExecute handler, you can use thread communication methods like TEvent and TMonitor to wait until there is data for the client.
TMonitor is available since Delphi 2009 and provides methods (Wait, Pulse and PulseAll) to send / receive notifications with mininmal CPU usage.
TIdTCPServer
requires anOnExecute
event handler assigned by default. To get around that, you would have to derive a new class fromTIdTCPServer
and override its virtualCheckOkToBeActive()
method, and should also override the virtualDoExecute()
to callSleep()
. Otherwise, just assign an event handler and have it callSleep()
.This is not an effective use of
TIdTCPServer
, though. A better design is to not write your outbound data to clients from inside of yourSendMessage()
method directly. Not only is that error-prone (you are not catching exceptions fromWriteBuffer()
) and blocksSendMessage()
during writing, but it also serializes your communications (client 2 cannot receive data until client 1 does first). A much more effective design is to give each client its own thread-safe outbound queue, and then haveSendMessage()
put the data into each client's queue as needed. You can then use theOnExecute
event to check each client's queue and do the actual writing. This way,SendMessage()
does not get blocked anymore, is less error-prone, and clients can be written to in parallel (like they should be).Try something like this: