• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 494
  • Last Modified:

TIdTCPClient help with receiving text sent back

Hi.

I am making a small application that talks to a third part program that uses plain text to reply via a TCP connection.

I can get a single line of data that is returned with the following lines.
memo1.lines.add(IdTCPClient.IOHandler.ReadLn);
But, some of the time the third part application will post some details without me requesting it.
I still need to capture those details posted.
All data sent from the application is in plain text and varies in length.

Question is, How do i capture the full reply from the server into a memo or listbox and also how do i keep monitoring for details it sends to my application.
Can someone post a example?
Thanks.
0
satmanuk
Asked:
satmanuk
  • 3
  • 3
  • 2
1 Solution
 
MerijnBSr. Software EngineerCommented:
I'd strongly advice you to switch from Indy to ICS (http://www.overbyte.be/frame_index.html?redirTo=/products/ics.html).

ICS is a tcp suite just like Indy, but works async (where as Indy works sync).
This means you can just make an event for when you receive data and handle it like you 'normally'.
0
 
satmanukAuthor Commented:
I have got ICS installed on here, but is there any chance of a example on how to do this, Its been driving me nutts today.

Most of the examples i have seen are to in dept and not commented to well to be able to follow it.
TCP programming is new to me so i hope i can pick it up as i go.

Cheers for the fast reply.

0
 
MerijnBSr. Software EngineerCommented:
this simple example assumes that there already is a connection, it just handles the data.
Is this enough?


unit Unit13;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, WSocket, StdCtrls;

type
  TForm13 = class(TForm)
    WSocket1: TWSocket;
    procedure WSocket1DataAvailable(Sender: TObject; ErrCode: Word);
  private
   fReceiveBuffer: string;

   procedure CheckMessages;
    procedure HandleMessage(Message: string);
  public
    { Public declarations }
  end;

var
  Form13: TForm13;

implementation

uses
  StrUtils;

{$R *.dfm}

procedure TForm13.HandleMessage(Message: string);
begin
 // handle your messages here
end;

procedure TForm13.CheckMessages;
const CRLF = #13#10;
var CurMsg: string;
    SepPos: integer;
begin
 // see if we got a complete message in our buffer
 SepPos := Pos(CRLF, fReceiveBuffer);

 if SepPos <> 0 then
 begin
  // copy this message
  CurMsg := LeftStr(fReceiveBuffer, SepPos - 1);
  // remove it from the buffer
  Delete(fReceiveBuffer, 1, SepPos + 1);

  // handle the message
  HandleMessage(CurMsg);
 end;
end;

procedure TForm13.WSocket1DataAvailable(Sender: TObject; ErrCode: Word);
begin
 if ErrCode = 0 then
 begin
  // add the current received data to our buffer (messages might not come whole at once)
  fReceiveBuffer := fReceiveBuffer + WSocket1.ReceiveStr();

  // check for messages
  CheckMessages();
 end;
end;

end.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
TheRealLokiSenior DeveloperCommented:
If you want an indy solution you can take a look at my demos
http://sourceforge.net/projects/internetdemos
"TCP Clients:Interactive Thread"
ther's one for Indy 9 and Indy 10
basically, you need keep trying to read from the buffer in a loop
I use a thread for this so that it does not impact on the visual performance.
Something like this :-

            timedout := false;
            TCPClientInsideThread.ReadTimeout := 2000; // 2 seconds
            try
                repeat
                try
                    S := ReceiveStringWithLogging; // just does a ReadLN followed by logging to the screen
                except
                    on EIDReadTimeout do
                      timedout := true;
                    on e: exception do
                    begin
                        ThreadLogMessage(lmtError, ldNone, 'unspecified command error. ' + E.Message);
                        break;
                    end;
                end;
                until terminated or timedout; // terminated is just because this code is from my thread. You could use anopther flag if you like
            finally
                TCPClientInsideThread.ReadTimeout := 0; // set back to infinite
            end;

wrap that up in another loop if you like, or use it in a thread as i have
hope that helps,
Loki

0
 
TheRealLokiSenior DeveloperCommented:
oh btw, personally, I'd recommend using a non-blocking component set such as Francois Piette's ICS as MerijnB suggests :-)
0
 
satmanukAuthor Commented:
MerijnB.

I seem to have it working now, But could you show me how to do the collecting messages in a threed?
I have not used threeds befor but beleive i will need this running in a threed to stop the main application lagging.

Thanks



 unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, OverbyteIcsWndControl, OverbyteIcsWSocket,StrUtils;

type
  TForm1 = class(TForm)
    WSocket: TWSocket;
    Button1: TButton;
    Memo1: TMemo;
    StateLed: TShape;
    procedure Button1Click(Sender: TObject);
    procedure WSocketSessionClosed(Sender: TObject; ErrCode: Word);
    procedure WSocketSessionConnected(Sender: TObject; ErrCode: Word);
    procedure WSocketDataAvailable(Sender: TObject; ErrCode: Word);
  private
    { Private declarations }
    fReceiveBuffer: string;
    procedure CheckMessages;
    procedure HandleMessage(Message: string);
   public
    { Public declarations }
  end;

var
  Form1: TForm1;

Const
  CRLF = #13#10;

implementation

{$R *.dfm}

procedure TForm1.HandleMessage(Message: string);
begin
 memo1.lines.add(Message);
end;

procedure TForm1.CheckMessages;
var
  CurMsg: string;
  SepPos: integer;
begin
 // see if we got a complete message in our buffer
 SepPos := Pos(CRLF, fReceiveBuffer);

if SepPos <> 0 then
 begin
  // copy this message
  CurMsg := LeftStr(fReceiveBuffer, SepPos - 1);
  // remove it from the buffer
  Delete(fReceiveBuffer, 1, SepPos + 1);
  // handle the message
  HandleMessage(CurMsg);
 end;
end;

procedure TForm1.WSocketDataAvailable(Sender: TObject; ErrCode: Word);
begin
 if ErrCode = 0 then
 begin
  // add the current received data to our buffer (messages might not come whole at once)
  fReceiveBuffer := fReceiveBuffer + WSocket.ReceiveStr();
  // check for messages
  While length(fReceiveBuffer) <> 0 do  CheckMessages();
 end;
end;

procedure TForm1.WSocketSessionClosed(Sender: TObject; ErrCode: Word);
begin
 Memo1.Lines.Add('Session Closed');
 StateLed.Brush.Color := clRed;
end;

procedure TForm1.WSocketSessionConnected(Sender: TObject; ErrCode: Word);
Var
 Username,password:string;
begin
 Memo1.Lines.Add('Session Connected');
 StateLed.Brush.Color := clLime;
 Memo1.Lines.add('Trying to log on');
 Username:='admin2';
 Password:='admin2';
 WSocket.SendLine( 'login:' +  Username + CRLF + ':'+Password);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
 Memo1.Lines.Add('Connecting to server...');
 WSocket.Addr := REMOVED;
 WSocket.Port := REMOVED;
 WSocket.Connect;
end;

end.
0
 
MerijnBSr. Software EngineerCommented:
Unless you are going to process _very_ much data, you do not have to put this in a thread.
Your 'main application' is not being bothered with the collecting of data, only with the handling of it.

I should give this a try and see if you notice any lagging.
0
 
TheRealLokiSenior DeveloperCommented:
If you read the documentation of ICS, Francois Piette isn't a fan of threads anyway :-)
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

  • 3
  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now