How can I wait for a string from a server with IdTCPClient?

10,555

Solution 1

TIdTelnet is a multithreaded component. It has an internal thread that continuously reads from the socket, firing the TIdTelnet.OnDataAvailable event whenever a buffer of data is available.

TIdTelnet is a TIdTCPClient descendant. Look in the IdTelnet.pas source file to see how it is implemented. You can do something similar in your own code, calling TIdIOHandler.ReadLn() in your own thread, eg:

type
  TMyThread = class(TThread)
  private
    FConn: TIdTCPConnection;
  protected
    procedure Execute; override;
  public
    constructor Create(AConn: TIdTCPConnection); reintroduce;
  end;

constructor TMyThread.Create(AConn: TIdTCPConnection);
begin
  inherited Create(False);
  FConn := AConn;
end;

procedure TMyThread.Execute;
var
  S: String;
begin
  while not Terminated do
  begin
    S := FConn.IOHandler.ReadLn(...);
    ...
  end;
end;

var
  Thread: TMyThread = nil;

procedure TForm1.ConnectButtonClick(Sender: TObject);
begin
  IdTCPClient1.Connect;
  try
    Thread := TMyThread.Create(IdTCPClient1);
  except
    IdTCPClient1.Disconnect;
    raise;
  end;
end;

procedure TForm1.DisconnectButtonClick(Sender: TObject);
begin
  if Assigned(Thread) then Thread.Terminate;
  try
    IdTCPClient1.Disconnect;
  finally
    if Assigned(Thread) then
    begin
      Thread.WaitFor;
      FreeAndNil(Thread);
    end;
  end;
end;

If you don't want to use a thread, then you can use a timer instead. To make sure your timer thread (such as the main thread) is not blocked unnecessarily, use the TIdIOHandler.CheckForDataOnSource() method with a small timeout whenever the TIdIOHandler.InputBuffer is empty, before then calling TIdIOHandler.ReadLn() only when data is available, eg:

procedure TForm1.ConnectButtonClick(Sender: TObject);
begin
  IdTCPClient1.Connect;
  ReadTimer.Enabled := True;
end;

procedure TForm1.DisconnectButtonClick(Sender: TObject);
begin
  ReadTimer.Enabled := False;
  IdTCPClient1.Disconnect;
end;

procedure TForm1.ReadTimerElapsed(Sender: TObject);
var
  S: String;
begin
  if IdTCPClient1.IOHandler.InputBufferIsEmpty then
  begin
    IdTCPClient1.IOHandler.CheckForDataOnSource(10);
    if IdTCPClient1.IOHandler.InputBufferIsEmpty then Exit;
  end;
  S := IdTCPClient1.IOHandler.ReadLn(...);
  ...
end;

Solution 2

A syncronous solution for IdTCPClient

I was looking for a solution which would "wait" for the response, this seems to work quite well for what I'm doing if I play with the readTimeOut setting and disconnect the client from the Server side after a WriteLn from the server. Based on the readTimeOut this will disconnect after that is reached with the IoHandler.ReadLn();

function sendTCPCommand(command: string): string;
var
 lines, lineInput: String;
 tcpClient : TIdTcpClient;

begin
  tcpClient := TidTcpClient.Create(nil);
  try
    with tcpClient do
    begin
      Port := 80;
      Host := 127.0.0.1;

      try
        ReadTimeOut := 5000; //Adjust according to demands, each readln event uses it
        lines := '';
        connect;
        IoHandler.WriteLn(command);
        lineInput := IoHandler.ReadLn();
        while (lineInput <> '') do
        begin
          lines := lines + lineInput;
          lineInput := IoHandler.ReadLn();
          if (lineInput = '') then
          begin
            Disconnect; //ignores timeout when finished reading or getting nothing
          end;
        end;
        Result := lines;
      except
        On E:Exception do
        begin
          Result := E.Message;
        end;
      end;
    end;
  finally
    tcpClient.Free;
  end;
end;
Share:
10,555
SadeghAlavizadeh
Author by

SadeghAlavizadeh

Java, C#, Delphi and JavaScript programmer and teacher. Technical lead and senior developer in a lovely team. Start programming in 1994 with GW-Basic... ;D

Updated on June 04, 2022

Comments

  • SadeghAlavizadeh
    SadeghAlavizadeh almost 2 years

    I have a problem with IdTelnet (indy 10.1). I can't read the data from a server in Unicode mode. and now I want to write the telnet terminal with IdTCPClient.

    The server sometimes send one line and sometimes more and more lines. But there is not a fixed time between sending.

    Now my problem is that when I must read data from InBuffer.

    Or when I must use the ReadLn function to read the data from the server, how many times must I run the ReadLn?