HTTP.Get zawiesza watek

0

Cześć wszystkim. Piszę program i mam problem z zawieszaniem wątku przy pobieraniu strony.
Jest wątek, który tworzy klasę i za jej pomocą pobiera stronę. Wygląda to mniej więcej tak:

type
  TWatek = class(TThread)
  private
    MojaKlasa : TLogowanie;
	id : integer;
  protected
    procedure Execute; override;
  public
    constructor Create(aid : integer);
  end;
  
  
constructor TWatek.Create(aid : integer);
begin
  inherited Create(False);
  self.id := aid;
  MojaKlasa := TLogowanie.Create;
end;

procedure TWatek.Execute;
begin
  FreeOnTerminate := true;
  
  while (true)
  begin
    MojaKlasa.zaloguj();
	MojaKlasa.wyslij(jakasstrona);
	MojaKlasa.wyslij(jakasstrona2);
    MojaKlasa.wyloguj();
	sleep(15*60*1000);
  end;
  MojaKlasa.Free;
end;

Klasa Logowanie wygląda tak

type TLogowanie = class
  function zaloguj() : boolean;
  function Wyslij(link : string) : string;
  procedure Wyloguj();
private
  HTTP : TidHTTP;
public
  constructor Create();
  destructor Free();
end;



constructor TLogowanie.Create();
begin
  HTTP := TidHTTP.Create(nil);
  HTTP.ProtocolVersion:=pv1_1;
  HTTP.HandleRedirects:=True;
  HTTP.AllowCookies:=true;
  HTTP.Request.UserAgent:='Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36';
  HTTP.Request.Accept := 'text/javascript, text/html, application/xml, text/xml, */*';
  HTTP.Request.ContentType := 'application/x-www-form-urlencoded; charset=UTF-8';
  HTTP.RedirectMaximum:= 15;
  HTTP.ConnectTimeout := 15000;
  HTTP.ReadTimeout := 15000;
  HTTP.Request.ContentLength := -1;
  HTTP.Request.ContentRangeEnd := -1;
  HTTP.Request.ContentRangeInstanceLength := -1;
  HTTP.Request.ContentRangeStart := -1;
end;

function TLogowanie.zaloguj();
begin
  HTTP.POST(strona, zmienne);
  result:= True;
end;

function TLogowanie.wyloguj();
begin
  HTTP.GET(strona);
end;

function TLogowanie.Wyslij(link : string) : string;
var
  tresc : string;
begin
  tresc := HTTP.Get(link);
  result := tresc;
end;

destructor TLogowanie.Free();
begin
  HTTP.Free;
end;

Program łączy się na kilka stron i ma około 15 minut przerwy i robi wszystko od początku. Dziwne jest to że program potrafi działać bez zawieszenia jakieś 10 godzin a później zawiesić się trzy razy w ciągu godziny. Zawiesza się HTTP.GET lub HTTP.POST. Nie wykonuje się kod który dalej w klasie i w wątku. Mogę stworzyć osobny wątek, który co parę minut sprawdza czy pierwszy wątek się nie zawiesił i w razie potrzeby wywalić go i włączyć od nowa ale to raczej nie jest najlepsze rozwiązanie. Dlaczego tak się dzieje? Jest coś co mogę ustawić w HTTP żeby tego uniknąć? Gdyby chociaż wywalało błąd i szło dalej.

0

weź to w try except i zaloguj sobie gdzieś błąd. Na 99% strona nie odpowiada a nie zabezpieczyłeś programu na taki wypadek. BTW jak cokolwiek w wątku pobocznym wywali błąd to nie jest on przekazywany do głównego wątku tylko wątek poboczny jest ubijany. Dlatego przy programowaniu wielowątkowym takie ważne jest zabezpieczenie miejsc, które mogą rzucać wyjątkami

0

Nie wiedziałem, że wątek poboczny pada przy błędzie.
Teraz mam coś takiego

try
    kod := HTTP.Get(stron);
    except
    on E: EIdHTTPProtocolException do
      ShowMessage('HTTP status code: ' + IntToStr(E.ErrorCode) + sLineBreak +
          'Error message' + E.Message);

    on E: EIdException do
        ShowMessage('Exception class: ' + E.ClassName + sLineBreak +
          'Error message: ' + E.Message);

    on E: Exception do
      ShowMessage('Error message' + E.Message);
  end;

Raz pokazało mi Read Timeout
Wywaliłem z kodu HTTP.ReadTimeout := 15000; i teraz pokazało EIdConnClosedGracefully.
Na necie znalazłem, że pomóc może TIdHTTP.Disconnect(False) i TIdHTTP.IOHandler.InputBuffer.Clear().
Zostawię program i poczekam czy pokaże jakieś błędy i postaram się je rozwiązać. Dzięki za odpowiedz.

1

EIdConnClosedGracefully to nie jest błąd jako taki - to po prostu zakończenie połączenia przez drugą stronę. Czyli po prostu czasami strona nie odpowiada i powinieneś to uwzględnić jako normalne zachowanie - zignorować i ponowić próbę

0

EIdConnClosedGracefully to "cichy" wyjątek który generuje Indy w przypadku o którym pisał @abrakadaber i występuje on tylko pod IDE (znaczy normalnie nie ma nawet komunikatu błędu chyba że sam chcesz go wyświetlić jak to zrobiłeś) można też zrobić aby nie pojawiał się nawet pod IDE dodając EIdConnClosedGracefully a nawet od razu EIdSilentException do ignorowanych Tools -> Debugger Options na zakładce Language Exceptions.

Z zamrażaniem ja sobie radzę w ten sposób że w wątku wykonującym operacje przed każdym wysłaniem żądania POST czy tam GET pobieram czas w moim przypadku zrobiłem sobie właściwość LastActiveTime gdzie ten czas zapisuję a po żądaniu sprawdzam IdHTTP1.ResponseCode jeżeli inne niż 200 zakańczam wątek i robię wznowienie połączenia. Gdy uruchamiam wątek w zmiennej fCurrentThreadHandle zapisuję sobie jego uchwyt i uruchamiam timer w którym pilnuję czasu a w timerze:

procedure TfrmMain.antifreezeTimerTimer(Sender: TObject);
var
  ExitCode: Dword;
  LastTime: TDateTime;
begin
  if GetExitCodeThread(fCurrentThreadHandle, ExitCode) then 
  begin
    if ExitCode = STILL_ACTIVE then //czy watek jest aktywny
    begin
      try
        LastTime:= DownloadThread.LastActiveTime;
        if CompareDateTime(LastTime, IncSecond(Now, - 180))  < 0 then //czy przekroczono czas
        begin
          DownloadThread.Suspend; //zawieszenie i wznowienie wątku powoduje jego "odmrożenie" (dziwne ale działa)
          DownloadThread.Resume;
        end;
      except
      end;
    end
    else
    begin
      antifreezeTimer.Enabled:= False;
      fCurrentThreadHandle:= 0;
    end;
  end
  else
    antifreezeTimer.Enabled:= False;
end;

Nie jest to może jakieś super genialne rozwiązanie ale sie sprawdza.

1 użytkowników online, w tym zalogowanych: 0, gości: 1