Indy 10 / TidTCPServer / Streaming from one client to many - delphi

I would like to know a good approach to handle "one to many" communication using Indy 10 TidTCPServer ie receive a stream from a client, and immediately write this data to all connected "viewers". The point here is that the source of the data comes from a client (not the server).
On the OnExecute procedure, I can't write to other connected clients from there and using LockList just freezes the current thread so it doesn't help...
Should I use a different thread to handle buffer exchange?
Buffer data is mainly bytes, clients are already connected, let's say 3 (#1 sends, #2 and #3 should receive)
Here's what's in my OnExecute procedure:
var
lst: Tlist;
[­..]
lst := idTcpServer1.Contexts.LockList;
try
for i := 1 to lst.Count-1 do
begin
try
TIdContext(lst.Items[i]).Connection.IOHandler.Write('Test');
except
end;
end;
finally
idTcpServer1.Contexts.UnlockList;
end;

TCP does not support broadcasting, you have to use UDP or Multicast for that. So your only option is to loop through the Contexts list each time you have something to send.
You can certainly do that in the OnExecute event, if you don't mind slowing down the sending client. Assuming you do not want to do that, then yes, you have to move the loop to another thread. You could have the OnExecute handler put each received block of data into a thread-safe queue and then have the broadcasting thread extract data from the queue and send it to connected clients as needed.
As an added measure, you could also give each client its own individual thread-safe queue for outbound data, have the broadcast thread (or even OnExecute directly) put each block of data into each client's queue, then have the OnExecute event push a client's queue to the client when its not busy doing something else. That way, broadcasting and sending of data blocks is done in parallel so any given client does not block any other clients from receiving data in a timely manner, since each client runs in its own thread.

Related

How to start Indy TCP Server with parameters?

I am building a TCP Server using Indy 10 (from Delphi 2009). In OnExecute event I need to acces some data from the main thread. It is possible to pass that data to the server thread when I start it ? The server is started with IdTCPServer1.Active:=True; so I don't see how I can pass some parameters.
It is not possible to pass extra parameters to TIdTCPServer. Your server event handlers will have to retrieve the data from the main thread when needed.
To keep track of per-connection data across events, you can use the TIdContext.Data property, or derive a custom class from TIdServerContext and assign it to the TIdTCPServer.ContextClass property. For instance, your OnConnect event handler can retrieve the latest data from the main thread using TIdSync or TThread.Synchronize(), and then cache it in the context for OnExecute to use.

Indy10 TCP client deliver reply to sending procedure

I am making a client program in Delphi 7 with Indy 10.
It must connect to the server with TIdTCPClient and keep alive the connection for sending and getting commands and replies until the program is closed.
The server can maintain only one constant connection per client to send info-messages.
TIdTCPClient is listening through a reading thread.
QUESTION:
I am sending a request to the server (using WriteLn) from some procedure to get a list of strings, for example. How can I get the answer (reply) for that request in the same procedure, without leaving it? Like using TIdHTTP.
I see 2 solutions:
making the request from one procedure and handle it in other - the code and logic will be more complicated.
for each request in a procedure, create a new TIdTCPClient (Connect, WriteLn, ReadLn, Disconnect, Free) and handle request. But I do not like this solution as it causes large overhead.
Since a reading thread is involved, it does complicate things a little. The reading thread needs to be the one to receive all of the replies and then it can dispatch them to handlers as needed.
Your first solution is fine, if you don't mind breaking up your code. This is the simplest solution, and the best one if the main thread is the one making the requests. You should never block the main thread.
As you mentioned, your second solution is not a very good one.
Another solution would be to create a TEvent for each request, and put each request into a list/queue somewhere. Have the reading thread find and signal the appropriate event when a response is received. The sending procedure can then wait on the event until it is signaled (TThread.Synchronize() works this way, for example). If the procedure is running in the main thread, use MsgWaitForMultipleObjects() to do the wait, so you can still service the main message queue while waiting.

Best pratice ro handle Indy TCP Server execute in Delphi Xe3

When receiving data from a indy TCPServers execute method i generally handle it by reading the data by doing the following:
AContext.Connection.IOHandler.ReadLn;
and then process the data in the execute method. Most of the data that comes through are small JSon strings.
In the future i will need to handle larger data chunks and was wondering what the best pratice is to do so?
Is it a good idea to add the incoming data to a TidContext class and process it using some worker thread? Any thoughts or code samples would be appreciated. I use Indy 10 and Delphi XE3
The OnExecute event is already triggered in a worker thread, so it doesn't matter how long it takes to receive the data. As long at the data only has 1 (CR)LF at the end of it, ReadLn() will not care how long the string actually is (subject to the IOHandler's MaxLineAction and MaxLineLength properties, which you can tweak if needed). However, if the data has more than 1 (CR)LF in it, then you will have to either:
transmit the string length before transmitting the actual string, then use ReadLongInt() and ReadString() instead of ReadLn() in the receiving code.
terminate the string with a different delimiter than (CR)LF at the end, and then pass that delimiter to ReadLn() so it knows when to stop reading.
If the server has to receive incoming requests at a guaranteed rate, without blocking the consumers by slow request processing, saving the big data chunks to a datastore (file, database) for later processing could be a solution.
This would make the HTTP server thread available for the next request as soon as possible.
It also allows to do the data processing by multiple worker servers, with load balancing.

Delphi Service to Listen to TCP or UDP

Reading this question:
Delphi Windows Service Design, I saw that most designer use this code below in the OnExecute of the TService or in the TThread method, in order to keep service alive.
while not Terminated do
begin
// do something
end;
But what if I need (and I do) to create a service to respond (using Indy) to messages sent by the main application in order to send back some authentication data, what do I do with this code, ignore it or put some Sleep() in it?
Indy's TIdTCPServer and TIdUDPServer components are multi-threaded, so you don't really need to use the TService.OnExecute event at all. You could just activate them in the TService.OnStart event and deactivate them in the TService.OnStop event, and then assign handlers to the TIdTCPServer.OnExecute and TIdUDPServer.OnUDPRead events as needed. Both events are already looped for you, so you don't need a while not Terminated loop in them. Just read/process one request as needed, then exit, and wait for the next event to repeat. Let the server handle any exceptions that are raised. And keep in mind that TIdUDPServer has a ThreadedEvent property that is False by default, so you should set it to True inside a service.

Is it possible to call TidSMTP.Connect once and then from threads call TidSMTP.Send?

I am writing an app that sends e-mail messages using Indy.
Every message is sent by a thread.
Currently I am connecting to TidSMTP inside the thread, so for sending 10 mails, I need 10 threads and I connect 10 times.
Is it safe (which are the drawbacks?) of having a single TidSMTP (outside of the thread), call Connect once and then call TidSMTP.Send inside the thread?
Will TidSMTP manage thing correctly?
Note: the idea is to avoid to connect every time (if possible), in case of many emails to be sent it could be an advantage. (does it makes sense to get worried for this, or calling Connect in every thread is pefectly ok?).
Why don't you use only 1 thead in which you have a TIdSMTP and a TList in which you store TIdMessage's and after each send you free the TIdMessage from the list, in this case you avoid overhead and keep it simple.
What if you want to send 200 e-mails, well if you start 200 threads then your application will use over 200 Mb only for those 200 threads not to mention that there can be problems starting that many threads in your application.
Bottom line add a TList in which you temporarily store prepared TIdMessages and inside the thread a while loop that will check if the list has any messages to send, if it has then grab, send and remove from list.
Technically, you can call Connect() in one thread and then call Send() in other threads. However, you would have to serialize access to Send(), otherwise the sending threads can overlap each other and corrupt the SMTP communication. Dorin's suggestion to move all of the SMTP traffic to a single thread with a queue is the best choice. However, the queue itself needs to be accessed in a thread-safe manner, so using a plain TList or TQueue by itself it not good enough. Either use TThreadList (or Indy's own TIdThreadSafeList) instead of TList, or wrap the TQueue with a separate TCriticalSection.

Resources