Newsgroup: it.comp.lang.delphi
Titolo: Strano comportamento di TIdIcmpClient + GetTickCount

Sto facendo un programmino, più che altro per fare esercizio con i thread, 
che pinga tutti gli ip in una listview e ne mostra i risultati per ogni 
item.
Il programma, sorprendentemente (per le mie capacità), funziona, però non 
capisco perché dopo il primo ping (per ogni elemento), che ritorna dei 
valori abbastanza corretti, quelli successivi invece mi riportano valori 
impossibili, tipo 10ms per pingare 
www.google.it
.
Non capisco se il problema è dato dal fatto di usare GetTickCount (in 
effetti non capisco neanche perché nell'IdIcmpClient non ci sia una 
proprietà che restituisca il tempo impiegato dal ping...) o cos'altro.
Il programma è molto semplice, fa un ciclo tra gli item, e per ogni item 
crea un thread che a sua volta crea un IdIcmpClient; quest'ultimo fa il 
ping, calcola il tempo occorso e, tramite Synchronize, lo scrive nella 
listview. Dopo di questo chiamo Terminate (e il thread viene Free-ato perché 
durante la creazione del thread avevo impostato FreeOnTerminate := True).
Se volete posto il codice.
-- 
Jax
Ogni Mattina in Africa, una gazzella si sveglia, sa che deve correre più in 
fretta del leone o verrà uccisa. Ogni mattina in Africa, un leone si 
sveglia, sa che deve correre più della gazzella, o morirà di fame. Quando il 
sole sorge, non importa se sei un leone o una gazzella: è meglio che cominci 
a correre. 

 proprietà che restituisca il tempo impiegato dal ping...) o cos'altro.
Mi rispondo da solo: esiste MsRoundTripTime, che avevo scartato 
prematuramente in quanto mi ritornava 0 all'inizio... ma non era colpa sua. 
Domani provo a usare questa proprietà al posto del GetTickCount, dovrebbe 
funzionare.
-- 
Jax
Il sogno è l'infinita ombra del Vero. (Pascoli) 

 proprietà che restituisca il tempo impiegato dal ping...)
Certo che ce l'ha...
Il codice seguente è scritto al volo (potrebbe contenere errori):
unit Unit1;
interface
uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
Forms,
   Dialogs, IdBaseComponent, IdComponent, IdRawBase, IdRawClient,
   IdIcmpClient, StdCtrls, Buttons;
type
   PEchoData = ^TEchoData;
   TEchoData = record
     Msg: array [Byte] of Char;
   end;
   TPingThread = class(TThread)
   private
     FWindow: HWND;
     FIdIcmpClient: TIdIcmpClient;
   protected
     procedure Execute; override;
   public
     constructor Create(AWindow: HWND; AHostToPing: String);
     destructor Destroy; override;
   end;
   TForm1 = class(TForm)
     Memo1: TMemo;
     Memo2: TMemo;
     procedure FormCreate(Sender: TObject);
   private
     procedure WMCustomMessage(var Msg: TMessage); message WM_USER+100;
   public
     { Public declarations }
   end;
var
   Form1: TForm1;
implementation
{$R *.dfm}
{ TPingThread }
constructor TPingThread.Create;
begin
   FWindow := AWindow;
   FIdIcmpClient := TIdIcmpClient.Create(nil);
   FIdIcmpClient.Host := AHostToPing;
   FIdIcmpClient.ReceiveTimeout := 5000;
   inherited Create(False);
end;
destructor TPingThread.Destroy;
begin
   FIdIcmpClient.Free;
   inherited;
end;
procedure TPingThread.Execute;
var
   I: Integer;
   Msg: String;
   P: PEchoData;
begin
   for I := 0 to 9 do
   begin
     Msg := '---';
     try
       try
         FIdIcmpClient.Ping;
         case FIdIcmpClient.ReplyStatus.ReplyStatusType of
           rsEcho: Msg := Format('host %s replied in %d millisec.', [
              FIdIcmpClient.ReplyStatus.FromIpAddress,
              FIdIcmpClient.ReplyStatus.MsRoundTripTime]);
           rsError: Msg := 'Unknown error.';
           rsTimeOut: Msg := 'Timed out.';
           rsErrorUnreachable: Msg := Format('Host %s: destination 
network unreachable.',
              [FIdIcmpClient.ReplyStatus.FromIpAddress]);
         end;
       except
         on E: Exception do
           Msg := E.Message;
       end;
     finally
       P := GetMemory(SizeOf(TEchoData));
       StrPCopy(P^.Msg, Msg);
       PostMessage(FWindow, WM_USER+100, Cardinal(P), 0);
       Sleep(1000);
     end;
   end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
   I: Integer;
begin
   Randomize;
   Memo1.Lines.Add('
www.google.it
');
   Memo1.Lines.Add('
www.altavista.it
');
   Memo1.Lines.Add('paperino');
   Memo1.Lines.Add('192.168.0.123');
   Memo1.Lines.Add('127.0.0.1');
   Memo1.Lines.Add('
www.libero.it
');
   for I:= 0 to Memo1.Lines.Count-1 do
     with TPingThread.Create(Handle, Memo1.Lines.Strings[I]) do
       FreeOnTerminate := True;
end;
procedure TForm1.WMCustomMessage(var Msg: TMessage);
var
   P: PEchoData;
begin
   P := PEchoData(Msg.wParam);
   Memo2.Lines.Add(P^.Msg);
   FreeMemory(P);
end;
end.

 funzionare.
GetTickCount() non è comunque significativa per elaborazioni 
multi-thread, perché si rifa ad una risorsa unica in comune, che è 
l'uptime di Windows.

 Il codice seguente è scritto al volo (potrebbe contenere errori):
Grazie Andrea, questo codice mi sarà molto utile (e mi fa capire, nonostante 
anni e anni di delphi, che ne so ancora troppo poco :) ).
-- 
Jax
L'innocenza cominciò cor prim'omo, e lì rimase. (G. Belli) 

 unit Unit1;
Non è che mi daresti una mano a debuggare il tuo codice? :)
Mi spiego meglio, ho fatto un banale copia/incolla prima di implementarlo 
nella mia applicazione, e i risultati sono stati (ah, ho anche ridotto il 
numero di ping da 10 a 1 per ogni host):
host 127.0.0.1 replied in 47 millisec.
host 127.0.0.1 replied in 47 millisec.
host 195.210.91.83 replied in 1281 millisec.
host 195.210.91.83 replied in 1375 millisec.
host 195.210.91.83 replied in 1406 millisec.
Socket Error # 11001 Host not found.
Sembra che i 6 ping li faccia, però agli host che vuole lui e in modo un po' 
sconclusionato.
E' quasi la stessa cosa che capitava a me ieri... ma non ho la più pallida 
idea di dove possa essere la magagna. Non è che IdIcmpClient non va bene per 
questo scopo?
Un'altra cosa: io usavo Synchronize per scrivere i risultati nella form, c'è 
un motivo particolare per cui tu invece usi PostMessage?
-- 
Jax
Bisogna spegnere la prepotenza più che un incendio. (Eraclito) 

 un motivo particolare per cui tu invece usi PostMessage?
Questione di scelte: preferisco usare i messaggi per la comunicazione 
tra thread, perché non devo preoccuparmi di serializzare le operazioni 
(la serializzazione è in mano al sistema operativo che gestisce le code 
dei messaggi) ed è, a mio parere, più semplice, più affidabile, più 
efficiente.

 più efficiente.
Ok, grazie.
-- 
Jax
Un best-seller è la tomba dorata d'un talento mediocre. (Logan Pearsall
Smith) 

>>modo un po' sconclusionato.
Scusa! ti ho mandato io il codice sbagliato, la versione senza 
sincronizzazione.  TIdIcmpClient non è thread-safe (come c'è da 
aspettarsi, visto che la libreria Indy è scritta veramente coi piedi) e 
bisogna che i thread siano sincronizzati.  Con la versione seguente, 
invece, i risultati sono quelli che corretti che ti dicevo:
[PING ] Thread (tid= 1524): pinging www.google.it...
[PING ] Thread (tid= 3620): pinging www.altavista.it...
[PING ] Thread (tid=  564): pinging paperino...
[PING ] Thread (tid= 2652): pinging 192.168.0.123...
[PING ] Thread (tid=  852): pinging 127.0.0.1...
[PING ] Thread (tid= 3324): pinging www.libero.it...
[REPLY] Thread (tid= 1524): host 66.249.93.99 replied in 105 millisec.
[REPLY] Thread (tid=  852): host 127.0.0.1 replied in 0 millisec.
[REPLY] Thread (tid= 3620): host 66.94.234.13 replied in 243 millisec.
[REPLY] Thread (tid= 3324): host 195.210.91.83 replied in 31 millisec.
[ERROR] Thread (tid=  564): Socket Error # 11001 Host not found.
[REPLY] Thread (tid= 2652): Timed out.
// ----------------------------------------------------------------
unit Unit1;
interface
uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
Forms,
   Dialogs, IdBaseComponent, IdComponent, IdRawBase, IdRawClient,
   IdIcmpClient, StdCtrls, Buttons, SyncObjs;
type
   PEchoData = ^TEchoData;
   TEchoData = record
     Msg: array [Byte] of Char;
   end;
   TPingThread = class(TThread)
   private
     FWindow: HWND;
     FIdIcmpClient: TIdIcmpClient;
     FCritSect: TCriticalSection;
     procedure SendMsg(S: String);
   protected
     procedure Execute; override;
   public
     constructor Create(ACritSect: TCriticalSection; AWindow: HWND; 
AHostToPing: String);
     destructor Destroy; override;
   end;
   TForm1 = class(TForm)
     Memo1: TMemo;
     Memo2: TMemo;
     procedure FormCreate(Sender: TObject);
     procedure FormDestroy(Sender: TObject);
   private
     FCritSect: TCriticalSection;
     procedure WMCustomMessage(var Msg: TMessage); message WM_USER+100;
   public
     { Public declarations }
   end;
var
   Form1: TForm1;
implementation
{$R *.dfm}
{ TPingThread }
constructor TPingThread.Create;
begin
   FCritSect := ACritSect;
   FWindow := AWindow;
   FIdIcmpClient := TIdIcmpClient.Create(nil);
   FIdIcmpClient.Host := AHostToPing;
   FIdIcmpClient.ReceiveTimeout := 5000;
   FreeOnTerminate := True;
   inherited Create(False);
end;
destructor TPingThread.Destroy;
begin
   FIdIcmpClient.Free;
   inherited;
end;
procedure TPingThread.Execute;
var
   I: Integer;
   Msg: String;
begin
   for I := 0 to 0 do
   begin
     Msg := '---';
     try
       try
         SendMsg(Format('[PING ] Thread (tid=%5d): pinging %s...', 
[ThreadID, FIdIcmpClient.Host]));
         FCritSect.Acquire;
         try
           FIdIcmpClient.Ping;
           case FIdIcmpClient.ReplyStatus.ReplyStatusType of
             rsEcho: Msg := Format('[REPLY] Thread (tid=%5d): host %s 
replied in %d millisec.', [
                ThreadId,
                FIdIcmpClient.ReplyStatus.FromIpAddress,
                FIdIcmpClient.ReplyStatus.MsRoundTripTime]);
             rsError: Msg := Format('[REPLY] Thread (tid=%5d): Unknown 
error.', [ThreadId]);
             rsTimeOut: Msg := Format('[REPLY] Thread (tid=%5d): Timed 
out.', [ThreadId]);
             rsErrorUnreachable: Msg := Format('[REPLY] Thread 
(tid=%5d): Host %s: destination network unreachable.',
                [ThreadId, FIdIcmpClient.ReplyStatus.FromIpAddress]);
           end;
         finally
           FCritSect.Release;
         end;
       except
         on E: Exception do
           Msg := Format('[ERROR] Thread (tid=%5d): %s', [ThreadId, 
E.Message]);
       end;
     finally
       SendMsg(Msg);
     end;
   end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
   I: Integer;
begin
   Randomize;
   Memo1.Lines.Add('
www.google.it
');
   Memo1.Lines.Add('
www.altavista.it
');
   Memo1.Lines.Add('paperino');
   Memo1.Lines.Add('192.168.0.123');
   Memo1.Lines.Add('127.0.0.1');
   Memo1.Lines.Add('
www.libero.it
');
   FCritSect := TCriticalSection.Create;
   for I:= 0 to Memo1.Lines.Count-1 do
     TPingThread.Create(FCritSect, Handle, Memo1.Lines.Strings[I]);
end;
procedure TForm1.WMCustomMessage(var Msg: TMessage);
var
   P: PEchoData;
begin
   P := PEchoData(Msg.wParam);
   Memo2.Lines.Add(P^.Msg);
   FreeMemory(P);
end;
procedure TPingThread.SendMsg(S: String);
var
   P: PEchoData;
begin
   P := GetMemory(SizeOf(TEchoData));
   StrPCopy(P^.Msg, S);
   PostMessage(FWindow, WM_USER+100, Cardinal(P), 0);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
   FCritSect.Free;
end;
end.

 invece, i risultati sono quelli che corretti che ti dicevo:
Grazie, funziona da dio. Non ho capito a cosa serva il TCriticalSection, ma 
mi leggerò la guida.
Il randomize è lì per sbaglio, spero :)
-- 
Jax
Il primo uomo d'una donna o è stato un grande amore o un grande errore. 

 Il randomize č lě per sbaglio, spero :)
Sě, ormai sě, prove tecniche.  Avevo fatto un test facendo attendere un 
thread per un tempo random prima di terminare, in modo da far 
accavallare piů thread in modo casuale.

 Le critical section sono strumenti di sincronizzazione tra thread
Grazie x la spiegazione.
Per chiudere il cerchio, che componenti useresti al posto degli Indy, in
questo caso? Avevo sentito parlar bene dei componenti di Piette...
-- 
Jax
====
Meglio dormire con un cannibale astemio che con un cristiano ubriaco.
(Melville)

 questo caso? Avevo sentito parlar bene dei componenti di Piette...
Non dico assolutamente che gli Indy siano inusabili ma che hanno 
sicuramente dei difetti.  Non conosco altri componenti, nel senso che 
non li ho mai usati.  Per questo tipo di applicazioni ho sempre usato il 
C/C++ in cui l'utilizzo dei BSD socket č piuttosto standardizzato.