Entwickler-Ecke

Internet / Netzwerk - url aufrufen und abbruch nach 3 sekunden oder bei 404


idefix123456 - Mi 13.04.11 19:09
Titel: url aufrufen und abbruch nach 3 sekunden oder bei 404
Hallo,

Nach stundenlangem Suchen und ausprobieren, frage ich hier einfach mal nach. Ich habe die untere Funktion. Damit rufe ich eine URL im Hintergrund auf. Als Result wird mir dann der JSON übergabe wert als String zurückgeliefert. Ziel ist es von Delphi aus Daten über die URL an das php script auf meinen Server zu schicken, dieser wertet das ganze aus, und schickt einen json string an Delphi zurück. Soweit funktioniert das Ganze auch super! Vorausgesetzt es besteht eine Internet Verbindung!

Und da liegt mein Problem. Wenn keine Verbindung besteht, dann Versucht die funktion quasi in einer Endlosschleife die URL zwanghaft doch zu öffnen, was ja nicht geht, und deswegen hängt sich immer die komplette Form auf wodurch ich das Programm beenden muss!


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
  function LoadURL(url: string): string;
    var
      HttpLoader: TIdHttp;
  begin
    HttpLoader := TIdHttp.Create;
    try
      result := HttpLoader.Get(url);
    finally
      HttpLoader.Free;
    end;
  end;




Jetzt versuche ich seit Stunden einen Timeout mit in der funktion einzubauen. Also das die funktion 3 sekunden lang versuchen kann die URL zu öffnen, wenn das nicht gelingt wird die funktion abgebrochen, und false zurückgeliefert!


Habe schon:

Idhttp1.ReadTimeout := 3000;
Idhttp1.ConnectTimeout := 3000;

ausprobiert, das funktioniert aber nicht, da dennoch nicht abgebrochen wird.



Wer kann helfen?
Danke


jaenicke - Mi 13.04.11 19:54

Da Indy mit blocking sockets arbeitet, wird das nicht so gehen. Das ist aber ohnehin wenig sinnvoll so, da du deinen Hauptthread so blockierst.

Lagere das ganze am besten in einen Thread aus. Dafür erzeuge dir zuerst mit CreateEvent ein Ereignis. Dieses gibst du dann zusammen mit der Adresse an den Thread weiter. Danach wartest du mit MsgWaitForMultipleObjects (mit QS_ALLINPUT) auf dein Ereignis (ohne Timeout, damit nichts blockiert) und wartest bis du entweder ein Ergebnis bekommst oder die 3 Sekunden herum sind. In der Schleife kannst du Application.ProcessMessages aufrufen.

Im Thread wiederum hast du diesen Quelltext. Wenn du die Seite erfolgreich abrufen konntest, speicherst du das Ergebnis in einer public Eigenschaft des Threads und löst dann mit SetEvent das Ereignis aus.

Außerhalb des Threads bekommst du jetzt von MsgWaitForMultipleObjects gemeldet, dass das Ereignis ausgelöst wurde und du kannst das Ergebnis aus der Eigenschaft des Threads lesen.

Auf diese Weise kannst du ohne deine GUI zu blockieren warten. Du kannst natürlich auch gar nicht warten und auf das Signal aus dem Thread warten, dass er fertig ist, aber das würde die Bestimmung des Timeouts schwieriger machen, es sei denn der Thread verschluckt das Ergebnis einfach nach dem Ende des Timeouts. Ganz wie du möchtest. ;-)


Thom - Mi 13.04.11 20:23

Vielleicht hilft das [http://stackoverflow.com/questions/3314878/is-there-a-way-to-set-response-timeout-for-indy-tidhttp-gets] weiter.


idefix123456 - Do 14.04.11 15:09

Hi,

Danke für die Antworten...

Da ich noch nicht so richtig mit Threads gearbeitet habe, hab ich mich mal ein wenig bei google drüber schlau gemacht und folgenden code zusammengeschustert (ungetestet)


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
var
  GlobalURL: string;



procedure OpenURLResult(json: string);
begin

  ShowMessage(json);

end;
  


procedure OpenURLThread();

  function LoadURL(url: string): string;
    var
      HttpLoader: TIdHttp;
  begin
    HttpLoader := TIdHttp.Create;
    try
      result := HttpLoader.Get(url);
    finally
      HttpLoader.Free;
    end;
  end;
  
  var
    json: string;
begin

  json := LoadURL(GlobalURL);
  OpenURLResult(json);

end;



procedure OpenURL(url: string);
  var
    ThreadID: DWORD;
begin
  GlobalURL := url;
  CreateThread(nil0, TFNThreadStartRoutine(@OpenURLThread), nil0, ThreadID);
end;




Wenn ich jetzt OpenURL('http://www.google.de') ausführe, sollte eingentlich ein einem neuen Thread das ganze gestartet werden, und wenn eine Verbindung hergestellt ist, sollte die procedure OpenURLResult ausgeführt werden.

Jetzt habe ich noch 2 fragen, und zwar wenn das ganze nun in einem Nebenthread anstatt in dem Haupttread gestartet wird und es besteht Keine Verbindung zum Internet, dann müsste sich aber doch der Nebenthread dennoch aufhängen anstatt wie zuvor die Komplette Form (da ja trotzdem kein Abbruch nach 3 skeunden). Das sich der Thread Aufhängt sollte doch eigentlich egal sein, aber Verbraucht das dann nicht doch dennoch Resourcen???

Außerdem könntest du das mit dem MsgWaitForMultipleObjects und das "speicherst du das Ergebnis in einer public Eigenschaft des Threads und löst dann mit SetEvent das Ereignis aus" nochmal etwas genauer erklären!


Danke


haentschman - Do 14.04.11 21:45

Hallo...

da du noch nie mit Threads gearbeitet hast empfehle ich dir TThread. Dieses ist etwas leichter zu verstehen als der direkte Weg über die API. Suche mal hier im Forum danach.


jaenicke - Fr 15.04.11 06:16

user profile iconidefix123456 hat folgendes geschrieben Zum zitierten Posting springen:
Außerdem könntest du das mit dem MsgWaitForMultipleObjects und das "speicherst du das Ergebnis in einer public Eigenschaft des Threads und löst dann mit SetEvent das Ereignis aus" nochmal etwas genauer erklären!
Ganz kurz als Gerüst, weil ich bald zur Arbeit los muss:
Im Hauptthread:

Delphi-Quelltext
1:
2:
3:
MyEvent := CreateEvent(nil, True, False, nil);
MyThread := TUrlLoadThread.Create(MyUrl, MyEvent);
Wait...


Im Thread:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
property LoadResult: string read FLoadResult;

constructor Create(const AUrl: string; AMyEvent: THandle);
begin
  FMyUrl := AMyUrl;
  FMyEvent := AMyEvent;
  inherited Create(False);
end;

procedure Execute;
begin
  // ladeversuch
  ...
  // erfolgreich:
  FLoadResult := ...
  SetEvent(FMyEvent);
end;

Für die Wait-Funktion schau dir am besten das an:
http://www.delphipraxis.net/6620-delay.html