We help IT Professionals succeed at work.

Indy Client problem when receiving text

plinho
plinho asked
on
Medium Priority
1,205 Views
Last Modified: 2008-01-09
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?
Comment
Watch Question

TheRealLokiSenior Developer
CERTIFIED EXPERT

Commented:
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
TheRealLokiSenior Developer
CERTIFIED EXPERT

Commented:
You could also stick the TCPClient inside a thread (with the above or similar code) and make a
.OnStringReceived() synchronized event

Author

Commented:
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?
TheRealLokiSenior Developer
CERTIFIED EXPERT

Commented:
I suspect you are doing
WriteLn(s) or Readln instead of
TCPClient.WriteLn(s) or TCPClient.ReadLn

Author

Commented:
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...

Author

Commented:
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?
Senior Developer
CERTIFIED EXPERT
Commented:
The server code for sending is incomplete
you need to tell the server "which" client to send to , or to all...

  with TCPServer do
    begin
    Writeln(edit1.text);
    end;
The  above will not work - there is no TCPServer.WriteLn
You should change it to :-

    with TCPServer.Threads.LockList do // Loop through all the connections
    try
        for i := 0 to Count - 1 do
        begin
              TIdPeerThread(Items[i]).Connection.Writeln(edit1.text); // write to each connection
        end;
     finally
        TCPServer.Threads.UnLockList;
     end;

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts

Author

Commented:
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?
TheRealLokiSenior Developer
CERTIFIED EXPERT

Commented:
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;

Author

Commented:
thanks =)
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.