2

i am very new to delphi and i have two projects written in delphi which must communicate - a client and a server. i have managed to get comms going for the connection and disconnection, but i can't get the client to send messages to the server at the click of a button - it appears that the messages do not make it to the server.

here is the client code:

unit my_client;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdAntiFreezeBase, IdAntiFreeze, IdTCPConnection,
  IdTCPClient, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack,
  IdBaseComponent, IdComponent;

type
  TForm1 = class(TForm)
    lstLog: TListBox;
    Label1: TLabel;
    txtData: TEdit;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    txtServer: TEdit;
    txtPort: TEdit;
    btnConnect: TButton;
    btnSend: TButton;
    btnDisconnect: TButton;
    IdAntiFreeze1: TIdAntiFreeze;
    Client: TIdTCPClient;
    procedure btnConnectClick(Sender: TObject);
    procedure btnSendClick(Sender: TObject);
    procedure btnDisconnectClick(Sender: TObject);
  private
    ip: string;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnConnectClick(Sender: TObject);
begin
    Client.Host := txtServer.Text;
    Client.Port := StrToInt(txtPort.Text);
    ip := txtServer.Text + ':' + txtPort.Text;
    lstLog.Items.Add('attempting to connect to ip [' + ip + ']');
    with Client do
    begin
        try //try connecting
          Connect;
          lstLog.Items.Add('successfully connected to ip [' + ip + ']');
          try //try getting data
            lstLog.Items.Add('response: [' + Client.IOHandler.ReadLn() + ']');
            BtnConnect.Enabled := False;
            BtnSend.Enabled := True;
            btnDisconnect.Enabled := True;
          except
            lstLog.Items.Add('cannot send data to ip [' + ip + ']');
            Client.Disconnect();
          end; //end try getting data
        except
          lstLog.Items.Add('cannot connect to ip [' + ip + ']');
        end; //end try connecting
    end; //end with
end; //end begin

procedure TForm1.btnSendClick(Sender: TObject);
begin
  lstLog.Items.Add('sending data: [' + txtData.Text + ']...');
  with Client do
  begin
    try
      IOHandler.Write(txtData.Text);
      lstLog.Items.Add('sent data: [' + txtData.Text + ']');
    except
      lstLog.Items.Add('failed to send data [' + ip + ']');
      Client.Disconnect();
      lstLog.Items.Add('Disconnect with ' + txtServer.Text + ' !');
      BtnConnect.Enabled := True;
      BtnSend.Enabled := False;
      btnDisconnect.Enabled := False;
    end;//end try
  end;//end with
end;

procedure TForm1.btnDisconnectClick(Sender: TObject);
begin
  Client.Disconnect();
  lstLog.Items.Add('disconnected from ip [' + ip + ']');
  BtnConnect.Enabled := True;
  BtnSend.Enabled := False;
  btnDisconnect.Enabled := False;
end;

end.

and here is my server code:

unit my_server;

interface

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

type
  TfrmTCPServer = class(TForm)
    edtPort: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    lbLog: TListBox;
    btnStart: TButton;
    btnStop: TButton;
    IdTCPServer: TIdTCPServer;
    procedure btnStartClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure IdTCPServerConnect(AContext: TIdContext);
    procedure IdTCPServerExecute(AContext: TIdContext);
    procedure IdTCPServerDisconnect(AContext: TIdContext);
    procedure IdTCPServerException(AContext: TIdContext;
      AException: Exception);
  private
  { Private declarations }
  public
  end;
var
  frmTCPServer: TfrmTCPServer;

implementation

uses
  StrUtils;

{$R *.dfm}

procedure TfrmTCPServer.btnStartClick(Sender: TObject);
begin
  IdTCPServer.DefaultPort := StrToInt(EdtPort.Text);
  IdTCPServer.Active := True;
  BtnStart.Enabled := False; //don't let it be clicked again
  BtnStop.Enabled := True; //let it be clicked again
  lbLog.Items.Add('server started');
end;

procedure TfrmTCPServer.btnStopClick(Sender: TObject);
begin
  if IdTCPServer.Active = true then
  begin
    IdTCPServer.Active := False;
    BtnStart.Enabled := True; //let it be clicked now
    BtnStop.Enabled := False; //don't let it be clicked again
    lbLog.Items.Add('server stopped');
  end
  else
  begin
    lbLog.Items.Add('server already stopped');
  end
end;

procedure TfrmTCPServer.IdTCPServerConnect(AContext: TIdContext);
begin
  try
    AContext.Connection.IOHandler.WriteLn('100: welcome to tcp test server');
    lbLog.Items.Add('server received connection from [' + Acontext.Connection.Socket.Binding.PeerIP + ']');
  except
    lbLog.Items.Add('got here2');
  end;
end;

procedure TfrmTCPServer.IdTCPServerExecute(AContext: TIdContext); //this is looped infinitely?
var
  client_data: string; //strCommand
begin
  with AContext.Connection do
  begin
    IOHandler.CheckForDataOnSource(10);
    if not IOHandler.InputBufferIsEmpty then
    begin
      client_data := IOHandler.ReadLn();
      IOHandler.WriteLn('received data [' + client_data + ']');
      lbLog.Items.Add('received data [' + client_data + '] from ' + Socket.Binding.PeerIP);
    end; //end if
  end; //end with
end;

procedure TfrmTCPServer.IdTCPServerDisconnect(AContext: TIdContext);
begin
  lbLog.Items.Add('client at ip [' + Acontext.Connection.Socket.Binding.PeerIP + '] has disconnected');
end;

procedure TfrmTCPServer.IdTCPServerException(AContext: TIdContext;
  AException: Exception);
begin
  lbLog.Items.Add('server exception: [' + AException.Message + ']');
end;

end.

the problem seems to lie in the IdTCPServerExecute procedure on the server. maybe i am not using it correctly, or maybe i need to set up some parameters before using this procedure?

mulllhausen
  • 4,225
  • 7
  • 49
  • 71

1 Answers1

4

Your server code is calling IOHandler.ReadLn(), which expects a (CR)LF after the text. Your client is calling IOHandler.Write(), which does not send a (CR)LF. The client needs to call IOHandler.WriteLn() instead.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • excellent. i used sLineBreak as per [this q&a](http://stackoverflow.com/questions/254407) – mulllhausen May 17 '13 at 01:21
  • `sLineBreak` is platform-dependant. It is a CRLF on Windows, but is a LF on other platforms. You really should not be using it for communications. Just use `WriteLn()` like I said: ` IOHandler.WriteLn(txtData.Text)`. It always sends a CRLF. If you insist on using `Write()`, then at least use Indy's own `EOL` instead, which is a CRLF on all platforms: `IOHandler.Write(txtData.Text+EOL)` – Remy Lebeau May 17 '13 at 01:46
  • ah i should have read your answer more carefully, i missed that. thanks again. – mulllhausen May 17 '13 at 02:09
  • i tired to run this project, server starting normally on port 801 ,but when the client connect button clicked it show this message : `attempting to connect to ip [127.0.0.1:801]` and after 2 seconds `cannot connect to ip [127.0.0.1:801]` , what can cause this ? – peiman F. Mar 14 '14 at 18:21
  • 1
    Is the server running on the same machine as the client? Did you verify with **netstat** or other tool that the server is actually listening on `127.0.0.1:801`? – Remy Lebeau Mar 14 '14 at 19:30
  • yes,both are on the same machine but there isnt listener on 801 port!! how can i fix the server?! i uploaded my client and server code here : `http://nilzoom.com/codes/p2p.rar` – peiman F. Mar 14 '14 at 20:51
  • @peimanF.: Double-check the `Port` property of each entry in the server's `Bindings` collection. You are setting the server's `DefaultPort` dynamically, but if the `Bindings` is not empty then changing the `DefaultPort` **does not** update the `Port` of existing `Bindings` entries. You have to either update them individually, or else `Clear` the `Bindings` so the server can recreate them when activated. When activating the server, you should loop through the `Bindings` logging each IP/Port that is actually being used. – Remy Lebeau Mar 15 '14 at 00:30
  • @peimanF.: Also, in your server event handlers, it is not thread-safe to call `lbLog.Items.Add()` directly. You need to sync with the main thread, such as via `TThread.Synchronize()`, `TThread.Queue()`, `TIdSync`, or `TIdNotify`, to access the UI safely, otherwise bad things can happen, including deadlocks and crashes. Also, in your `OnExecute` handler, get rid of `CheckForDataOnSource()` and `InputBufferIsEmpty()`. Call `ReadLn()` unconditionally and let it block until data arrives or a disconnect occurs. – Remy Lebeau Mar 15 '14 at 00:33
  • thank you remy , i added a new question here : `https://stackoverflow.com/questions/22422694/delphi-idtcpserver-not-listening` – peiman F. Mar 15 '14 at 10:45