I see problems with your client code.
You are assigning TCPClient.OnConnected
and TCPClient.OnDisconnected
event handlers after calling TCPClient.Connect()
. You should be assigning them before calling Connect()
.
you are assigning TCPClient.IOHandler.DefStringEncoding
after sending all of your data. You should be setting it before sending any data.
You are sending the .zip
file size as bytes, but then sending the actual file content using a TStringStream
. You need to use a TFileStream
or TMemoryStream
instead. Also, you can get the file size from the stream, you don't have to query the file size before then creating the stream.
You have a complete lack of error handling. If any exception is raised while btnRunClick()
is running, you are leaking your TIdTCPClient
object and not disconnecting it from the server.
I see some problems with your server code as well:
your OnCreate
event is activating the server before the Clients
list has been created.
various misuse of TThread.LockList()
and TThreadList.Unlock()
.
using InputBufferIsEmpty()
and TRTLCriticalSection
unnecessarily.
lack of error handling.
using TIdAntiFreeze
, which has no effect on servers.
Try this instead:
Client:
unit ComunicaClientForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,
IdAntiFreezeBase, Vcl.IdAntiFreeze, Vcl.Samples.Spin, Vcl.ExtCtrls,
IdComponent, IdTCPConnection, IdTCPClient, idGlobal;
type
TfrmComunicaClient = class(TForm)
memoIncomingMessages: TMemo;
IdAntiFreeze: TIdAntiFreeze;
lblProtocolLabel: TLabel;
Timer: TTimer;
grp1: TGroupBox;
grp2: TGroupBox;
btnRun: TButton;
chkIntervalado: TCheckBox;
spIntervalo: TSpinEdit;
lblFrequencia: TLabel;
lbl1: TLabel;
lbl2: TLabel;
lblNumberExec: TLabel;
procedure FormCreate(Sender: TObject);
procedure TCPClientConnected(Sender: TObject);
procedure TCPClientDisconnected(Sender: TObject);
procedure TimerTimer(Sender: TObject);
procedure chkIntervaladoClick(Sender: TObject);
procedure btnRunClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmComunicaClient: TfrmComunicaClient;
implementation
{$R *.dfm}
const
DefaultServerIP = '127.0.0.1';
DefaultServerPort = 7676;
procedure TfrmComunicaClient.FormCreate(Sender: TObject);
begin
memoIncomingMessages.Clear;
end;
procedure TfrmComunicaClient.TCPClientConnected(Sender: TObject);
begin
memoIncomingMessages.Lines.Insert(0,'Connected to Server');
end;
procedure TfrmComunicaClient.TCPClientDisconnected(Sender: TObject);
begin
memoIncomingMessages.Lines.Insert(0,'Disconnected from Server');
end;
procedure TfrmComunicaClient.TimerTimer(Sender: TObject);
begin
Timer.Enabled := False;
btnRun.Click;
Timer.Enabled := True;
end;
procedure TfrmComunicaClient.chkIntervaladoClick(Sender: TObject);
begin
Timer.Interval := spIntervalo.Value * 1000;
Timer.Enabled := True;
end;
procedure TfrmComunicaClient.btnRunClick(Sender: TObject);
var
Size : Int64;
fStrm : TFileStream;
NomeArq : String;
Retorno : string;
TipoRetorno : Integer; // 1 - Anvisa, 2 - Exception
TCPClient : TIdTCPClient;
begin
memoIncomingMessages.Lines.Clear;
TCPClient := TIdTCPClient.Create(nil);
try
TCPClient.Host := DefaultServerIP;
TCPClient.Port := DefaultServerPort;
TCPClient.ConnectTimeout := 3000;
TCPClient.OnConnected := TCPClientConnected;
TCPClient.OnDisconnected := TCPClientDisconnected;
TCPClient.Connect;
try
TCPClient.IOHandler.DefStringEncoding := TIdTextEncoding.UTF8;
TCPClient.IOHandler.WriteLn('SendArq'); // Sinaliza Envio
TCPClient.IOHandler.WriteLn('1'); // Envia CNPJ
TCPClient.IOHandler.WriteLn('email@gmail.com'); // Envia Email
TCPClient.IOHandler.WriteLn('12345678'); // Envia Senha
TCPClient.IOHandler.WriteLn('12345678901234567890123456789012'); // Envia hash
memoIncomingMessages.Lines.Insert(0,'Write first data : ' + DateTimeToStr(Now));
NomeArq := ExtractFilePath(Application.ExeName) + 'arquivo.zip';
fStrm := TFileStream.Create(NomeArq, fmOpenRead or fmShareDenyWrite);
try
Size := fStrm.Size;
TCPClient.IOHandler.WriteLn(IntToStr(Size));
if Size > 0 then begin
TCPClient.IOHandler.Write(fStrm, Size, False);
end;
finally
fStrm.Free;
end;
memoIncomingMessages.Lines.Insert(0,'Write file: ' + DateTimeToStr(Now) + ' ' +IntToStr(Size)+ ' bytes');
memoIncomingMessages.Lines.Insert(0,'************* END *********** ' );
memoIncomingMessages.Lines.Insert(0,' ');
// Recebe Retorno da transmissão
TipoRetorno := StrToInt(TCPClient.IOHandler.ReadLn);
Retorno := TCPClient.IOHandler.ReadLn;
//making sure!
TCPClient.IOHandler.ReadLn;
finally
TCPClient.Disconnect;
end;
finally
TCPClient.Free;
end;
lblNumberExec.Caption := IntToStr(StrToInt(lblNumberExec.Caption) + 1);
end;
end.
Server:
unit ComunicaServerForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
IdCustomTCPServer, IdTCPServer, IdScheduler, IdSchedulerOfThread,
IdSchedulerOfThreadPool, IdBaseComponent, IdSocketHandle, Vcl.ExtCtrls,
IdStack, IdGlobal, Inifiles, System.Types, IdContext, IdComponent;
type
TfrmComunicaServer = class(TForm)
txtInfoLabel: TStaticText;
mmoProtocol: TMemo;
grpClientsBox: TGroupBox;
lstClientsListBox: TListBox;
grpDetailsBox: TGroupBox;
mmoDetailsMemo: TMemo;
lblNome: TLabel;
TCPServer: TIdTCPServer;
ThreadManager: TIdSchedulerOfThreadPool;
procedure lstClientsListBoxClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure TCPServerConnect(AContext: TIdContext);
procedure TCPServerDisconnect(AContext: TIdContext);
procedure TCPServerExecute(AContext: TIdContext);
private
{ Private declarations }
procedure RefreshListDisplay;
procedure RefreshListBox;
public
{ Public declarations }
end;
var
frmComunicaServer: TfrmComunicaServer;
implementation
{$R *.dfm}
type
TClient = class(TIdServerContext)
public
PeerIP : string; { Client IP address }
HostName : String; { Hostname }
Connected, { Time of connect }
LastAction : TDateTime; { Time of last transaction }
end;
const
DefaultServerIP = '127.0.0.1';
DefaultServerPort = 7676;
procedure TfrmComunicaServer.FormCreate(Sender: TObject);
begin
TCPServer.ContextClass := TClient;
TCPServer.Bindings.Clear;
with TCPServer.Bindings.Add do
begin
IP := DefaultServerIP;
Port := DefaultServerPort;
end;
//setup TCPServer
try
TCPServer.Active := True;
except
on E: Exception do
ShowMessage(E.Message);
end;
txtInfoLabel.Caption := 'Aguardando conexões...';
RefreshListBox;
if TCPServer.Active then begin
mmoProtocol.Lines.Add('Comunica Server executando em ' + TCPServer.Bindings[0].IP + ':' + IntToStr(TCPServer.Bindings[0].Port));
end;
end;
procedure TfrmComunicaServer.FormClose(Sender: TObject; var Action: TCloseAction);
var
ClientsCount : Integer;
begin
with TCPServer.Contexts.LockList do
try
ClientsCount := Count;
finally
TCPServer.Contexts.UnlockList;
end;
if ClientsCount > 0 then
begin
Action := caNone;
ShowMessage('Há clientes conectados. Ainda não posso sair!');
Exit;
end;
try
TCPServer.Active := False;
except
end;
end;
procedure TfrmComunicaServer.TCPServerConnect(AContext: TIdContext);
var
DadosConexao : TClient;
begin
DadosConexao := TClient(AContext);
DadosConexao.PeerIP := AContext.Connection.Socket.Binding.PeerIP;
DadosConexao.HostName := GStack.HostByAddress(DadosConexao.PeerIP);
DadosConexao.Connected := Now;
DadosConexao.LastAction := DadosConexao.Connected;
(*
TThread.Queue(nil,
procedure
begin
MMOProtocol.Lines.Add(TimeToStr(Time) + ' Abriu conexão de "' + DadosConexao.HostName + '" em ' + DadosConexao.PeerIP);
end
);
*)
RefreshListBox;
AContext.Connection.IOHandler.DefStringEncoding := TIdTextEncoding.UTF8;
end;
procedure TfrmComunicaServer.TCPServerDisconnect(AContext: TIdContext);
var
DadosConexao : TClient;
begin
DadosConexao := TClient(AContext);
(*
TThread.Queue(nil,
procedure
begin
MMOProtocol.Lines.Add(TimeToStr(Time) + ' Desconnectado de "' + DadosConexao.HostName + '"');
end
);
*)
RefreshListBox;
end;
procedure TfrmComunicaServer.TCPServerExecute(AContext: TIdContext);
var
DadosConexao : TClient;
CNPJ : string;
Email : string;
Senha : String;
Hash : String;
Size : Int64;
FileName : string;
Arquivo : String;
ftmpStream : TFileStream;
Cmd : String;
Retorno : String;
TipoRetorno : Integer; // 1 - Anvisa, 2 - Exception
begin
DadosConexao := TClient(AContext);
Cmd := AContext.Connection.IOHandler.ReadLn;
if Cmd = 'SendArq' then
begin
CNPJ := AContext.Connection.IOHandler.ReadLn;
Email := AContext.Connection.IOHandler.ReadLn;
Senha := AContext.Connection.IOHandler.ReadLn;
Hash := AContext.Connection.IOHandler.ReadLn;
Size := StrToInt64(AContext.Connection.IOHandler.ReadLn);
// Recebe Arquivo do Client
FileName := ExtractFilePath(Application.ExeName) + 'Arquivos\' + CNPJ + '-Arquivo.ZIP';
fTmpStream := TFileStream.Create(FileName, fmCreate);
try
if Size > 0 then begin
AContext.Connection.IOHandler.ReadStream(fTmpStream, Size, False);
end;
finally
fTmpStream.Free;
end;
// Transmite arquivo para a ANVISA
Retorno := 'File Transmitted with sucessfull';
TipoRetorno := 1;
// Grava Log
fTmpStream := TFileStream.Create(ExtractFilePath(Application.ExeName) + 'Arquivos\' + CNPJ + '.log', fmCreate);
try
WriteStringToStream(ftmpStream, Retorno, TIdTextEncoding.UTF8);
finally
fTmpStream.Free;
end;
// Envia Retorno da ANVISA para o Client
AContext.Connection.IOHandler.WriteLn(IntToStr(TipoRetorno)); // Tipo do retorno (Anvisa ou Exception)
AContext.Connection.IOHandler.WriteLn(Retorno); // Msg de retorno
// Sinaliza ao Client que terminou o processo
AContext.Connection.IOHandler.WriteLn('DONE');
end;
end;
procedure TfrmComunicaServer.lstClientsListBoxClick(Sender: TObject);
var
DadosConexao: TClient;
Index: Integer;
begin
mmoDetailsMemo.Clear;
Index := lstClientsListBox.ItemIndex;
if Index <> -1 then
begin
DadosConexao := TClient(lstClientsListBox.Items.Objects[Index]);
with TCPServer.Contexts.LockList do
try
if IndexOf(DadosConexao) <> -1 then
begin
mmoDetailsMemo.Lines.Add('IP : ' + DadosConexao.PeerIP);
mmoDetailsMemo.Lines.Add('Host name : ' + DadosConexao.HostName);
mmoDetailsMemo.Lines.Add('Conectado : ' + DateTimeToStr(DadosConexao.Connected));
mmoDetailsMemo.Lines.Add('Ult. ação : ' + DateTimeToStr(DadosConexao.LastAction));
end;
finally
TCPServer.Contexts.UnlockList;
end;
end;
end;
procedure TfrmComunicaServer.RefreshListDisplay;
var
Client : TClient;
i: Integer;
begin
lstClientsListBox.Clear;
mmoDetailsMemo.Clear;
with TCPServer.Contexts.LockList do
try
for i := 0 to Count-1 do
begin
Client := TClient(Items[i]);
lstClientsListBox.AddItem(Client.HostName, Client);
end;
finally
TCPServer.Contexts.UnlockList;
end;
end;
procedure TfrmComunicaServer.RefreshListBox;
begin
if GetCurrentThreadId = MainThreadID then
RefreshListDisplay
else
TThread.Queue(nil, RefreshListDisplay);
end;
end.