Solved

Indy Client problem when receiving text

Posted on 2006-11-06
10
970 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?
0
Comment
Question by:plinho
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 5
  • 5
10 Comments
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 17885926
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
0
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 17885942
You could also stick the TCPClient inside a thread (with the above or similar code) and make a
.OnStringReceived() synchronized event
0
 

Author Comment

by:plinho
ID: 17893958
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?
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 17

Expert Comment

by:TheRealLoki
ID: 17901950
I suspect you are doing
WriteLn(s) or Readln instead of
TCPClient.WriteLn(s) or TCPClient.ReadLn
0
 

Author Comment

by:plinho
ID: 17905398
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...
0
 

Author Comment

by:plinho
ID: 17907906
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?
0
 
LVL 17

Accepted Solution

by:
TheRealLoki earned 125 total points
ID: 17908080
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;
0
 

Author Comment

by:plinho
ID: 17908956
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?
0
 
LVL 17

Expert Comment

by:TheRealLoki
ID: 17910035
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;
0
 

Author Comment

by:plinho
ID: 17910609
thanks =)
0

Featured Post

Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Dev express lookupcombo 3 44
Graphics32 under Delphi 10.1 Berlin 2 124
Not able to call Delphi XE10 dll function from Delphi 6.0 4 48
Wincontrol not (correctly) drawn 15 47
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Although Jacob Bernoulli (1654-1705) has been credited as the creator of "Binomial Distribution Table", Gottfried Leibniz (1646-1716) did his dissertation on the subject in 1666; Leibniz you may recall is the co-inventor of "Calculus" and beat Isaac…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

740 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question