Link to home
Start Free TrialLog in
Avatar of plinho
plinho

asked on

Indy Client problem when receiving text

Hello,

I'm making a server and a client using idTCPServer and idTCPClient [indy9]. What I want is to make the client send text to the server, wich is kind of easy, and the server send text to the client, wich I can't figure where is my error...

Here is the code of my Client App:


unit TCPClientUnt;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, ExtCtrls;

type
  TForm1 = class(TForm)
    TCPClient: TIdTCPClient;
    Edit3: TEdit;
    Button2: TButton;
    GroupBox1: TGroupBox;
    Label3: TLabel;
    Label4: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    Memo1: TMemo;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure TCPClientConnected(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure TCPClientDisconnected(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
tcpclient.Host:=edit1.Text;
tcpclient.Port:=strtoint(edit2.text);
tcpclient.Connect(1000);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  with TCPClient do
    begin
    WriteLn(edit3.Text);
    end;

end;

procedure TForm1.TCPClientConnected(Sender: TObject);
begin
timer1.enabled:=true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
aText: string;
begin
  with TCPClient do
    begin
    aText:=ReadLn('',10);
      if (aText <> '') then
        begin
        Memo1.Lines.add('Received: ' + aText);
        end;
    end;
end;

procedure TForm1.TCPClientDisconnected(Sender: TObject);
begin
timer1.Enabled:=false;
end;

end.

When I put this timer, once the Client connects to the Server, it freezes.... What is wrong with my code?
Avatar of TheRealLoki
TheRealLoki
Flag of New Zealand image

it is freezing because of the
aText:=ReadLn('',10);
line
Indy will wait at this point until the server sends a sting back
Just get your server to reply and you'll be away again
I am guessing you were trying to have it check for a string, waiting 10 ms and then carry on.
You can do the same thing by the folowing

uses IDException;
...
procedure TForm1.Timer1Timer(Sender: TObject);
var
aText: string;
begin
  aText := '';
  timer1.enabled := false;
  try
    TCPClient.ReadTimeout := 10;
    try
      aText:=TCPClient.ReadLn;
     if (aText <> '') then
      begin
        Memo1.Lines.add('Received: ' + aText);
      end;  
    except
      on EIDReadTimeout do ; //ignore timeouts
    end;
  finally
    timer1.enabled := true;
    TCPClient.ReadTimeout := 0; // reset to normal
  end;
end;

If this is the approach you are after, and you wish for a "non-blocking" implementation, I'd recommend ICS instead
You could also stick the TCPClient inside a thread (with the above or similar code) and make a
.OnStringReceived() synchronized event
Avatar of plinho
plinho

ASKER

ok, I did that, the client is not freezing anymore, but when I try to send message to it I get an "I/O error 105" why that?
I suspect you are doing
WriteLn(s) or Readln instead of
TCPClient.WriteLn(s) or TCPClient.ReadLn
Avatar of plinho

ASKER

Well, I'm not at home, but the server code to send a string is something like this:

procedure TForm1.button1onclick(Asender: TObject);
begin
  with TCPServer do
      WriteLn ( edit1.text );
end;

When i'm at home I'll post the code of the server and the client...
Avatar of plinho

ASKER

Well I'm at home
here is the SERVER code:

unit TCPServerUnt;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPServer;

type
  TForm1 = class(TForm)
    TCPServer: TIdTCPServer;
    Label1: TLabel;
    Edit1: TEdit;
    Memo1: TMemo;
    Button1: TButton;
    procedure TCPServerConnect(AThread: TIdPeerThread);
    procedure TCPServerExecute(AThread: TIdPeerThread);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.TCPServerConnect(AThread: TIdPeerThread);
begin
label1.Caption:= 'Connected';
Memo1.Lines.Add('Connected to ' + athread.Connection.Socket.Binding.IP);
end;

procedure TForm1.TCPServerExecute(AThread: TIdPeerThread);
var
aText: string;
begin
aText:=athread.Connection.ReadLn('',10);
  if (aText <> '') then
    begin
    Memo1.Lines.Add(atext);
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  with TCPServer do
    begin
    Writeln(edit1.text);
    end;
end;

end.



and here the CLIENT code:

unit TCPClientUnt;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection,
  IdTCPClient, ExtCtrls, IDException;

type
  TForm1 = class(TForm)
    TCPClient: TIdTCPClient;
    Edit3: TEdit;
    Button2: TButton;
    GroupBox1: TGroupBox;
    Label3: TLabel;
    Label4: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Button1: TButton;
    Memo1: TMemo;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure TCPClientConnected(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure TCPClientDisconnected(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
tcpclient.Host:=edit1.Text;
tcpclient.Port:=strtoint(edit2.text);
tcpclient.Connect(1000);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  with TCPClient do
    begin
    WriteLn(edit3.Text);
    end;

end;

procedure TForm1.TCPClientConnected(Sender: TObject);
begin
timer1.enabled:=true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
aText: string;
begin
aText:='';
timer1.Enabled:=false;
  try
    TCPClient.ReadTimeout:=10;
    try
    aText:=TCPClient.ReadLn;
      if (aText <> '') then
        begin
        Memo1.Lines.add('Received: ' + aText);
        end;
    except
      on EIdReadTimeout do //nothing
    end;
  finally
  timer1.enabled:=true;
  TCPClient.ReadTimeout:=0;
  end;
end;

procedure TForm1.TCPClientDisconnected(Sender: TObject);
begin
timer1.Enabled:=false;
end;

end.


What should I do?
ASKER CERTIFIED SOLUTION
Avatar of TheRealLoki
TheRealLoki
Flag of New Zealand image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of plinho

ASKER

Thanks TheRealLoki!! And could you answer a last question??
When I close the server without closing the connection the client gets A LOT of "Connection closed gracefully.". That's because of the timer isn't it? What can i do to prevent this?
put a check in teh OnTimer event for the exception, and close your connection. Then only have the timer "rearm" if the connection is alive

eg.

procedure TForm1.Timer1Timer(Sender: TObject);
var
  aText: string;
begin
  aText := '';
  timer1.enabled := false;
  try
    TCPClient.ReadTimeout := 10;
    try
      aText:=TCPClient.ReadLn;
     if (aText <> '') then
      begin
        Memo1.Lines.add('Received: ' + aText);
      end;
    except
      on EIDReadTimeout do ; //ignore timeouts
      on e: exception do
      begin
         timer1.enabled := false;
         TCPClient.Disconnect;
//         raise; <-- raise if you want to show the user, or do memo1.lines.add(e.message); to log etc...
      end
    end;
  finally
    TCPClient.ReadTimeout := 0; // reset to normal
    if TCPClient.Connected then
      timer1.enabled := true; // only rearm if connected
  end;
end;
Avatar of plinho

ASKER

thanks =)