Delphi – Indy idTCPServer and idTCPClient
This is a simple example for usign Nevrona Indy components in Delphi to create a small server application using TCP/IP protocol. TO simplify we can build an example where there is a Server Application that recive a string from a Client App. and then return that string to the Client , but we use streams to do that because the example can be reused to trasmit anithing else via stream.
In this example we use the TStringStream class that have TStream as ancestor and is very simple to use with strings.
Start with Delphi and create a simple form application , now we drag the TidTCPServer component on the main form , now we need to decide wich free TCP port for this service , in this app we use 9099 port. , here the simple source of Server App.:
unit Main_Unit_Server;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdBaseComponent, IdComponent, IdTCPServer, StdCtrls, Buttons, IdSocketHandle,
IdServerIOHandler, IdServerIOHandlerSocket, IdUDPBase, IdUDPServer,
IdAntiFreezeBase, IdAntiFreeze, IdMappedPortTCP, IdThreadMgr,
IdThreadMgrDefault;
type
TServer_Form = class(TForm)
TCP_Server: TIdTCPServer;
ListBox1: TListBox;
Start_Server_Button: TSpeedButton;
Stop_Server_Button: TSpeedButton;
Label1: TLabel;
Bind_IP: TEdit;
Bind_Port: TEdit;
ListBox2: TListBox;
IdAntiFreeze1: TIdAntiFreeze;
SpeedButton1: TSpeedButton;
IdThreadMgrDefault1: TIdThreadMgrDefault;
ckAutoReply: TCheckBox;
ckVideoResult: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure Start_Server_ButtonClick(Sender: TObject);
procedure Stop_Server_ButtonClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure TCP_Server__Connect(AThread: TIdPeerThread);
procedure TCP_ServerExecute(AThread: TIdPeerThread);
procedure TCP_ServerNoCommandHandler(ASender: TIdTCPServer;
const AData: String; AThread: TIdPeerThread);
procedure TCP_ServerConnect(AThread: TIdMappedPortThread);
procedure SpeedButton1Click(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure TCP_ServerDisconnect(AThread: TIdPeerThread);
private
procedure ShowClientsConnected;
function StopTheServer: Boolean;
{ Private declarations }
public
{ Public declarations }
ListaClient : TList;
end;
var
Server_Form: TServer_Form;
implementation
uses IdTCPConnection;
{$R *.dfm}
procedure TServer_Form.FormCreate(Sender: TObject);
begin
Top:=0;
Left:=0;
Start_Server_ButtonClick(Nil);
ListaClient := TList.Create;
end;
procedure TServer_Form.Start_Server_ButtonClick(Sender: TObject);
var Loc_Binding : TIdSocketHandle;
begin
if TCP_Server.Active then begin
Exit;
end;
try
TCP_Server.DefaultPort := 9099;
TCP_Server.Active:=True;
if ListBox1.Items.Count>10
then ListBox1.Items.Delete(0);
if TCP_Server.Active then
begin
ListBox1.Items.Add('Server started .... '+TCP_Server.Bindings.Items[0].IP+':'+IntToStr(TCP_Server.Bindings.Items[0].Port));
end
else
begin
ListBox1.Items.Add('ERROR. Cannot start server .... ');
Exit;
end;
except
ListBox1.Items.Add('ERROR. Setting-up server .... ');
Exit;
end;
end;
procedure TServer_Form.Stop_Server_ButtonClick(Sender: TObject);
begin
if not TCP_Server.Active then Exit;
try
TCP_Server.Active := False;
except
end;
end;
procedure TServer_Form.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
if Action=caFree then begin
if TCP_Server.Active then begin
Stop_Server_ButtonClick(Nil);
end;
end;
end;
procedure TServer_Form.TCP_Server__Connect(AThread: TIdPeerThread);
Var s : String;
begin
ShowClientsConnected;
if ListBox2.Items.Count>10
then ListBox2.Items.Delete(0);
ListBox2.Items.Add( 'Client is connected from '+
AThread.Connection.Socket.Binding.IP+':'+
IntToStr(AThread.Connection.Socket.Binding.Port) );
// imposto un buffer piccolo
AThread.Connection.RecvBufferSize := 65536 div 4;
AThread.Connection.SendBufferSize := 65536 div 4;
ListaClient.Add(AThread);
end;
// Execute è il metodo principe , che intercetta le chiamate dei Client
procedure TServer_Form.TCP_ServerExecute(AThread: TIdPeerThread);
Var
S,Resp : String;
Data : String;
I : Integer;
Ms : TStringStream;
Begin
Try
Try
Data := '';
Ms := nil;
Try
Ms := TStringStream.Create('');
Ms.Position := 0;
AThread.Connection.ReadStream(Ms);
Ms.Position := 0;
Data := Ms.DataString;
if ckVideoResult.Checked then
ListBox2.Items.Add(AThread.Connection.Socket.Binding.IP+' --> '+Data);
Except
On E:Exception do
Begin
ListBox2.Items.Add('Errore [1]: ' + E.Message);
End;
End;
If ckAutoReply.Checked then
Begin
MS := TStringStream.Create('Giovanni dice : ' + Data);
Ms.Position := 0;
AThread.Connection.WriteStream(MS,True,True);
Try Ms.Free; Except End;
End;
//Resp := TClientManager(AThread.Data).CommandParser(Data,True);
Except
On E:Exception do
ListBox2.Items.Add('Errore [2]: ' + E.Message);
End;
Finally
If ms <> nil then
Try Ms.Free; Except End;
End;
end;
// Sending a string to all connected clients
procedure TServer_Form.SpeedButton1Click(Sender: TObject);
Var
i:Integer;
ms : TStringStream;
begin
try
TCP_Server.Threads.LockList;
for i:=0 to ListaClient.Count-1 do
Begin
MS := TStringStream.Create('Invio massivo dati');
Ms.Position := 0;
TIdPeerThread(ListaClient[i]).Connection.WriteStream(MS,True,True);
Try Ms.Free; Except End;
End;
finally
TCP_Server.Threads.UnlockList;
end;
end;
procedure TServer_Form.FormCloseQuery(Sender: TObject;
var CanClose: Boolean);
begin
Try TCP_Server.Active := False; Except End;
CanClose := True;
end;
procedure TServer_Form.TCP_ServerDisconnect(AThread: TIdPeerThread);
begin
ListaClient.Remove(AThread);
end;
end.
The Client application is simple too, we create another app with delphi and drag the TidTCPClient specifing the same TCP port of the server and the addrres of the server :
unit Main_Unit_Client;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
StdCtrls, IdUDPBase, IdUDPClient, ExtCtrls, IdIntercept, IdSocks,
IdIOHandlerSocket, IdIOHandler, IdIOHandlerStream;
type
TForm1 = class(TForm)
TCP_Client: TIdTCPClient;
Label2: TLabel;
Label3: TLabel;
Host_Address: TEdit;
Host_Port: TEdit;
Connect_Button: TButton;
btnDisconnect: TButton;
ListBox1: TListBox;
Edit1: TEdit;
Button1: TButton;
ListBox2: TListBox;
pnlSemaforo: TPanel;
btnMultiSend: TButton;
ckVideoResult: TCheckBox;
Timer1: TTimer;
btn_startTimer: TButton;
procedure Connect_ButtonClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnDisconnectClick(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure TCP_ClientConnected(Sender: TObject);
procedure TCP_ClientDisconnected(Sender: TObject);
procedure btnMultiSendClick(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure btn_startTimerClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses DateUtils;
procedure TForm1.Connect_ButtonClick(Sender: TObject);
begin
if TCP_Client.Connected then begin
TCP_Client.Disconnect;
Exit;
end;
ListBox1.Clear;
ListBox2.Clear;
try
TCP_Client.Host :=Host_Address.Text;
TCP_Client.Port :=StrToInt(Host_Port.Text);
TCP_Client.Connect(1000);
ListBox1.Clear;
except
ListBox1.Items.Add('ERROR trapped while trying to connect');
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Top:=0;
Left:=Screen.Width-Width-30;
Hide;
//FormStyle:=fsStayOnTop;
Show;
end;
procedure TForm1.btnDisconnectClick(Sender: TObject);
begin
if TCP_Client.Connected then begin
TCP_Client.Disconnect;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
Var
s:String;
MS : TStringStream;
begin
if TCP_Client.Connected then begin
MS := TStringStream.Create(Edit1.Text);
MS.Position := 0;
TCP_Client.WriteStream(MS,true,true);
MS.Position := 0;
if ckVideoResult.Checked then
ListBox1.Items.Add('Sent ....: ' + MS.DataString);
Try MS.Free; Except End;
MS := TStringStream.Create('');
MS.Position := 0;
TCP_Client.ReadStream(MS);
MS.Position := 0;
if ckVideoResult.Checked then
ListBox1.Items.Add('Recived ....: ' + MS.DataString);
Try MS.Free; Except End;
end;
end;
procedure TForm1.TCP_ClientConnected(Sender: TObject);
begin
ListBox2.Items.Add('On Conneted');
pnlSemaforo.Color := clLime;
end;
procedure TForm1.TCP_ClientDisconnected(Sender: TObject);
begin
pnlSemaforo.Color := clRed;
end;
procedure TForm1.btnMultiSendClick(Sender: TObject);
Var
I,J : Integer;
N1,N2 : TDateTime;
begin
Try
I := StrToInt(InputBox('Nr. di comunicazioni : ', 'Nr. di comunicazioni : ', '100'));
Except
I := 10;
End;
N1 := now;
For J:=0 to I do
Button1Click(nil);
N2 := now;
ShowMessage('Millisecondi totali: ' + IntToStr(DateUtils.MilliSecondsBetween(n1,n2)) + ' millisecondi per operazione:' + IntToStr(DateUtils.MilliSecondsBetween(n1,n2) div I));
end;
procedure TForm1.Timer1Timer(Sender: TObject);
Var
s:String;
MS : TStringStream;
begin
if TCP_Client.Connected then
begin
Try
MS := TStringStream.Create('');
MS.Position := 0;
TCP_Client.ReadStream(MS);
MS.Position := 0;
if ckVideoResult.Checked then
ListBox1.Items.Add('Recived ....: ' + MS.DataString);
Try MS.Free; Except End;
Except
End;
end;
end;
procedure TForm1.btn_startTimerClick(Sender: TObject);
begin
Timer1.Enabled := True;
end;
end.