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.
Related
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.
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.
I have some clients that post xml / soap containers. I would like to build a tiny standalone indy web server app, that accepts any request and displays the content of the PostStream in a memo field. Unfortunately I don't know how to decode the PostStream. I get an exception using this code:
ARequestInfo.PostStream.Seek(0, soFromBeginning);
Memo1.Lines.LoadFromStream(ARequestInfo.PostStream);
I am using Delphi7 Enterprise
Not every request type uses the PostStream property, so you have to check if the PostStream is not nil before you attempt to use it.
Also, TIdHTTPServer is a multi-threaded component. Its events are triggered in worker threads, and it is not safe to access UI components from outside of the main thread, so you need to synchronize with the main thread when accessing the TMemo.
Update: If you need to always have a PostStream available, in older versions of Indy you can use the OnCreatePostStream event to create your own TStream object to use as the PostStream. In those versions, TIdHTTPServer manages PostStream objects that it creates, and sometimes frees them before giving them to you, but user-defined PostStream objects are not freed until after the request is finished being processed by you. However, in recent Indy 10 releases, there is a new OnDoneWithPostStream event that helps control when a PostStream gets freed for any given request, regardless of whether it is created by TIdHTTPServer or by you (in which case, you don't need to use the OnCreatePostStream event to extend the PostStream's lifetime). In all versions, an unfreed PostStream is always freed when the TIdHTTPRequestInfo object is freed.
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.
I am using indy's http server for a project so i have a few questions:
Does the event OnConnect get's executed even if one connects on a separate thread?
Can i update the vcl from the event OnConnect
If MaxNumberConnections is
set to 0 what does it exactly mean ?
Thanks.
1. Does the event OnConnect get's executed in a separate thread ?
Yes, the event will get fired as the other events do, even if you are running in a separate thread. The question remains to be answered by you is why. The TIdTCPServer already uses multithreading thus it can be used in a main thread context (on a form).
2. Can I update the VCL from the OnConnect event ?
Yes, but you will have to use some GUI synchronization practice, such as Synchronize method or e.g. message posting from the separated worker thread to your main one.
3. If MaxNumberConnections is set to 0 what does it exactly mean ?
Zero value assigned to the MaxConnections means there is no limit of connections at one time.