Solved

Indy Client problem when receiving text

Posted on 2006-11-06
10
990 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
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!

 
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

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
In this video, viewers are given an introduction to using the Windows 10 Snipping Tool, how to quickly locate it when it's needed and also how make it always available with a single click of a mouse button, by pinning it to the Desktop Task Bar. Int…
There's a multitude of different network monitoring solutions out there, and you're probably wondering what makes NetCrunch so special. It's completely agentless, but does let you create an agent, if you desire. It offers powerful scalability …
Suggested Courses
Course of the Month4 days, 18 hours left to enroll

635 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