Friso
Friso

Reputation: 2428

How to solve Indy socket error #111 connection refused between Delphi 2007 server and RAD XE7 client?

When I click on the button in the server the debugger shows two threads are started, I'm assuming one is the main thread and the other one is the server's thread, but the ServerExecute procedure is never executed, which I think causes the connection refused error.

How can I fix it?

Server code:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    IdTCPServer1: TIdTCPServer;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure ExecuteServer(AContext : TIdContext);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  IdTCPServer1.Bindings.Add.IP := '0.0.0.0';
  IdTCPServer1.Bindings.Add.Port := 2811;
  IdTCPServer1.OnExecute := ExecuteServer;
  IdTCPServer1.Active := True;
end;

procedure TForm1.ExecuteServer(AContext: TIdContext);
begin
  Sleep(Random(3000));
  Memo1.Lines.Add('Hello World');
  AContext.Connection.IOHandler.WriteLn('Hello World');
end;

end.

Client code:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, FMX.Layouts,
  FMX.Memo, FMX.StdCtrls, IdGlobal, IdIntercept;

type
  TpocForm1 = class(TForm)
    ButtonConnect: TButton;
    ButtonDisconnect: TButton;
    Memo1: TMemo;
    procedure ButtonConnectClick(Sender: TObject);
    procedure ButtonDisconnectClick(Sender: TObject);
    procedure AddLine(text : String);

  private

  public
    { Public declarations }
  end;

  TpocTCPClientThread = class(TThread)
    TCPClient: TIdTCPClient;
  protected
    procedure Execute; override;
    procedure AddLineToMemo;
    procedure Connect;
    procedure Disconnect;
  end;

var
  pocForm1: TpocForm1;

implementation
{$R *.fmx}
Const
  PC_IP = '192.168.32.252';
  PORT = 2811;

var
  thread: TpocTCPClientThread;

procedure TpocForm1.ButtonConnectClick(Sender: TObject);
begin
  Memo1.Lines.Add('Client connected with server');
  thread:= TpocTCPClientThread.Create(False);
end;

procedure TpocForm1.ButtonDisconnectClick(Sender: TObject);
begin
  thread.Terminate;
  thread.WaitFor;
  FreeAndNil(thread);
  Memo1.Lines.Add('Client disconnected from server');
end;

procedure TpocForm1.AddLine(text : String);
begin
  Memo1.Lines.Add(text);
end;


procedure TpocTCPClientThread.Execute();
begin
  Connect;

  while not Terminated do
  begin
    Synchronize(AddLineToMemo);
  end;

  Disconnect;
end;

procedure TpocTCPClientThread.AddLineToMemo;
begin
  pocForm1.AddLine(TCPClient.IOHandler.ReadLn(IndyTextEncoding_OSDefault()));
end;

procedure TpocTCPClientThread.Connect;
begin
  TCPClient := TIdTCPClient.Create;
  TCPClient.Host := PC_IP;
  TCPClient.Port := PORT;
  TCPClient.Connect;
end;

procedure TpocTCPClientThread.Disconnect;
begin
  TCPClient.Disconnect;
  TCPClient.Free;
end;


end.

edit I forgot to mention, the client is supposed to run on android.

Upvotes: 2

Views: 3342

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 595320

This is wrong:

IdTCPServer1.Bindings.Add.IP := '0.0.0.0';
IdTCPServer1.Bindings.Add.Port := 2811;

You are creating two separate bindings (which is why you get two threads started) - one bound to 0.0.0.0:0 and one bound to 0.0.0.0:2811.

Change it to this:

with IdTCPServer1.Bindings.Add do begin
  IP := '0.0.0.0';
  Port := 2811;
end;

Or this:

IdTCPServer1.Bindings.Add.SetBinding('0.0.0.0', 2811, IdIP_v4);

Or, simply set the TIdTCPServer.DefaultPort to 2811 and do not fill in the Bindings at all, then the server will create a default item bound to 0.0.0.0:2811 for you when activated.

With that said, there are some other problems with your code.

Server:

procedure TForm1.ExecuteServer(AContext: TIdContext);
begin
  Sleep(Random(3000));
  Memo1.Lines.Add('Hello World'); // <- must be synchronized!
  AContext.Connection.IOHandler.WriteLn('Hello World'); // <- default ASCII encoding used
end;

Client:

procedure TpocTCPClientThread.Execute();
begin
  Connect;

  while not Terminated do
  begin
    Synchronize(AddLineToMemo); // <- calling ReadLn() inside of Synchronize()
  end;

  Disconnect; // <- not called if an exception is raised
end;

TCPClient.IOHandler.ReadLn(IndyTextEncoding_OSDefault()) // <- OSDefault is not consistent across platforms

Change them to something more like this instead:

Server:

procedure TForm1.Button1Click(Sender: TObject);
begin
  ...
  IdTCPServer1.OnConnect := ConnectServer;
  ...
end;

procedure TForm1.ConnectServer(AContext: TIdContext);
begin
  AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8();
end;

procedure TForm1.ExecuteServer(AContext: TIdContext);
begin
  Sleep(Random(3000));
  TThread.Synchronize(nil,
    procedure
    begin
      Memo1.Lines.Add('Hello World');
    end
  );
  AContext.Connection.IOHandler.WriteLn('Hello World');
end;

Client:

procedure TpocForm1.ButtonConnectClick(Sender: TObject);
begin
  thread := TpocTCPClientThread.Create(False);
end;

procedure TpocForm1.ButtonDisconnectClick(Sender: TObject);
begin
  thread.Terminate;
  thread.WaitFor;
  FreeAndNil(thread);
end;

procedure TpocTCPClientThread.Execute();
begin
  Connect;
  try
    AddLineToMemo('Client connected with server');

    TCPClient.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8();

    while not Terminated do
    begin
      AddLineToMemo(TCPClient.IOHandler.ReadLn());
    end;
  finally    
    Disconnect;
    AddLineToMemo('Client disconnected from server');
  end;
end;

procedure TpocTCPClientThread.AddLineToMemo(text: string);
begin
  Synchronize(
    procedure
    begin
      pocForm1.AddLine(text);
    end
  );
end;

Upvotes: 3

Related Questions