0

I am using the ipWorks component TipwIPPort within a TThread. As I understand it, TipwIPPort is non-blocking.

I am familiar with creating threads where all processing within the Execute method is "blocking".

In this instance I need to connect to a remote server, then make subsequent calls using TIpwIPPort.DataToSend. I call the Connect method in the Execute function of the thread. But the OnConnected event is never fired.

What parameters and properties do I need to set (e.g. "CreateSuspended" passed to constructor, FreeOnTerminate value) so that I can control when to terminate the thread.

type TMyThread=class(TThread)
private
  IPPort1: TipwIPPort;
  procedure IPPort1Connected(Sender: TObject; StatusCode: Integer; const Description: String);
  procedure IPPort1DataIn(Sender: TObject; Text: String; EOL: Boolean);
end;



procedure TMyThread.IPPort1Connected(Sender: TObject; StatusCode: Integer; const Description: String);
begin
// never get here
  AppendToLog('Status Code in Connect:'+inttostr(StatusCode)+'; Description:'+Description);
  if StatusCode = 0 then begin
    //  send data to server using ipport1.datatosend.....
  end;
end;

procedure TMyThread.Execute;
begin
  appendtolog('TMyThread.Execute');
  IPPort1    := TipwIPPort.Create(nil);
  try
    With IPPort1 do begin
      EOL := #4;
      KeepAlive           := True;
      OnConnected         := IPPort1Connected;
      OnDataIn            := IPPort1DataIn;
    end;
    IPPort1.Connect('xxx.xxx.xxx.xxx',8888);
    appendtolog('done with execute');
  finally
  end;
end;

procedure TMyThread.IPPort1DataIn(Sender: TObject; Text: String; EOL: Boolean);
begin

  if (Pos('keytoendconnection',Text)>0)  then begin
    ipPort1.Disconnect;
    // Terminate the thread and free
  end;
end;

procedure TForm1.Button1Click(sender: TObject);
var
  myThread;
begin
  // what parameters and properties do I need to set to allow me to control when the thread is terminated???
  myThread := TMyThread.Create(True);
  mSouthernObject.FreeOnTerminate := False;
  mSouthernObject.Resume;
end;
Jan Doggen
  • 8,799
  • 13
  • 70
  • 144
M Schenkel
  • 6,294
  • 12
  • 62
  • 107
  • 2
    Recommended lecture http://stackoverflow.com/questions/4206377/which-is-the-correct-way-to-start-a-suspended-thread-in-delphi-2007 – RRUZ Aug 03 '15 at 20:22
  • After you connect, the thread returns immediately. You posted fake code too. Please show real code. An MCVE. You've asked over 100 questions here now. You should know not to post fake code by now. – David Heffernan Aug 03 '15 at 21:10
  • @DavidHeffernan - Sorry... I did try to make an example from my larger project to illustrate the issue. So after the "connect", and exit out of the "Execute" procedure, is the thread terminating? Is there a way that cascaded calls to the server be made and I control when the thread should terminate? – M Schenkel Aug 03 '15 at 22:11
  • You have to get these details right. It's really frustrating when we answer based on what's in the question and the asker says, "oh, but my code is different". – David Heffernan Aug 03 '15 at 22:14
  • @DavidHeffernan - Thanks for your assistance. But Remy Lebeau has answered it. – M Schenkel Aug 03 '15 at 22:15
  • I hope you don't ignore my advice. – David Heffernan Aug 03 '15 at 22:17
  • 1
    @DavidHeffernan - I will try better. But in some instances it could take hours to make a MCVE. Even Remy has called me on it. While what I provided is a "prototype", I seemed to at least have gotten across the underlying problem. Sorry - will do better next time. Thank you for your past contributions to my problems. – M Schenkel Aug 03 '15 at 22:21
  • Ok. FWIW, the act of cutting down code to an MCVE often helps you solve a problem. – David Heffernan Aug 03 '15 at 22:29

1 Answers1

3

If you read the documentation for the IPPort component, it states:

The operation of the component is almost completely asynchronous. All the calls except the ones that deal with domain name resolution operate through asynchronous messages (no blocking calls). The gain in performance is considerable when compared to using blocking calls.

As such, your thread needs its own message queue/loop to receive and dispatch those socket messages, eg:

type
  TMyThread = class(TThread)
  private
    IPPort1: TipwIPPort;
    procedure IPPort1Connected(Sender: TObject; StatusCode: Integer; const Description: String);
    procedure IPPort1DataIn(Sender: TObject; Text: String; EOL: Boolean);
  protected
    procedure Execute; override;
    procedure DoTerminate; override;
    procedure TerminatedSet; override;
  end;

procedure TMyThread.IPPort1Connected(Sender: TObject; StatusCode: Integer; const Description: String);
begin
  AppendToLog('Status Code in Connect:'+inttostr(StatusCode)+'; Description:'+Description);
  if StatusCode = 0 then begin
    //  send data to server using ipport1.datatosend.....
  end else begin
    // start a timer or something to issue WM_CONNECT again after a small delay, say 5-10 seconds at least...
  end;
end;

const
  WM_CONNECT = WM_APP+1;

procedure TMyThread.Execute;
var
  Message: TMsg;
begin
  appendtolog('TMyThread.Execute');
  IPPort1 := TipwIPPort.Create(nil);
  try
    IPPort1.EOL := #4;
    IPPort1.KeepAlive := True;
    IPPort1.OnConnected := IPPort1Connected;
    IPPort1.OnDataIn := IPPort1DataIn;
    PostThreadMessage(ThreadID, WM_CONNECT, 0, 0);
    while not Terminated do
    begin
      if not GetMessage(Message, 0, 0, 0) then Break;
      case Message.Msg of
        WM_CONNECT:
        begin
          IPPort1.Connect('xxx.xxx.xxx.xxx', 8888);
        end;
        //...
      else
        TranslateMessage(Message);
        DispatchMessage(Message);
      end;
    end;
  finally
    IPPort1.Free;
  end;
end;

procedure TMyThread.DoTerminate;
begin
  appendtolog('done with execute');
  inherited;
end;

procedure TMyThread.TerminatedSet;
begin
  PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
end;

procedure TMyThread.IPPort1DataIn(Sender: TObject; Text: String; EOL: Boolean);
begin
  if Pos('keytoendconnection', Text) > 0 then
  begin
    ipPort1.Disconnect;
    Terminate;
  end;
end;

private
  myThread: TMyThread;

procedure TForm1.Button1Click(sender: TObject);
begin
  myThread := TMyThread.Create(False);
end;

procedure TForm1.Button2Click(sender: TObject);
begin
  if myThread <> nil then
  begin
    myThread.Terminate;
    myThread.WaitFor;
    FreeAndNil(myThread);
  end;
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770