Indy TCP client in thread

1.5k views Asked by At

Following on from this thread, I am trying to make a C++Builder XE5 application with:

  • a main form
  • a TIdTCPClient
  • a TThread for that TIdTCPClient's connection, which opens the socket, sends a request, then listens for multiple responses indefinitely
  • actions that occur on the socket will cause updates on the main form

I have been unable to find any examples; the examples linked from the Indy Demos page all do not use any threads in their clients, so far as I can see.

My questions are:

  • Should the TIdTCPClient be on the main form (as a design time component), or should it be a member variable of the thread class?
  • In the events fired by the TIdTCPClient , does the code in the event handlers (which are member functions of my main form) need to be Synchronized?
  • Is it safe for both the main VCL thread and the client thread to make function calls on the TIdTCPClient object?

Currently I take actions on the main form in response to every event, and also in response to receiving data on the socket. At the moment my code is full of temporary variables and stub functions because Synchronize requires a void(void) closure and it is quite spaghetti. So I wonder if I am taking a fundamentally wrong approach.

1

There are 1 answers

2
Remy Lebeau On BEST ANSWER

Should the TIdTCPClient be on the main form (as a design time component), or should it be a member variable of the thread class?

Either will work fine. What is important is that you call Connect() and other I/O methods in the context of the worker thread (inside its Execute() method).

In the events fired by the TIdTCPClient , does the code in the event handlers (which are member functions of my main form) need to be Synchronized?

Yes, if they are accessing UI controls, or other shared data that must be protected.

Is it safe for both the main VCL thread and the client thread to make function calls on the TIdTCPClient object?

That depends on the particular calls (for instance, sending outbound data from the main thread while reading inbound data in the worker thread, at least while the client is connected), but I would not suggest you rely on that. You should just keep all of your client-related actions in the worker thread only.

At the moment my code is full of temporary variables and stub functions because Synchronize requires a void(void) closure and it is quite spaghetti.

You can use Indy's TIdSync/TIdNotify classes to help you keep that spaghetti code better organized. For instance, derive a class from TIdSync, move your variables into it, and override its virtual DoSynchronize() method to call your Form method(s) as needed. Then you can create an instance of thee class, populate its variables if needed, call its Synchronize() method, read its variables if needed, and then free it.

#include <IdSync.hpp>

class TMySync : public TIdSync
{
protected:
    virtual void __fastcall DoSynchronize();
public:
    // variables...
};

void __fastcall TMySync::DoSynchronize()
{
    // call Form methods, use variables as needed...
}

void __fastcall TMyThread::Execute()
{
    //...
    TMySync *sync = new TMySync;
    // set variables as needed...
    sync->Synchronize();
    // read variables as needed...
    delete sync;
    //...
}