Как надежно "парировать" собой и тормоза при чтении из Сети?

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

Как надежно "парировать" собой и тормоза при чтении из Сети?

Сообщение Alex2013 » 22.10.2022 13:41:08

Суть проблемы :
Несмотря на все ловушки и проверки ошибки ввода вывода при чтении текста веб-станиц и картинок к ней из интернета всеравно могут сильно затормозить работу программы.

Идеи обхода проблемы:
1 Сравнительно быстрая проверка существования конкретного файла для конкретного URL перед основным чтением.
Результат :
Частично помогает избежать совсем уж тяжелых сбоев парсинга (например когда сеть по невнятным причинам выкатывает какую-нибудь "страницу-заглушку" ) но в то же время дополнительный тормоз даже при нормальном чтении .

2 Попытка читать данные "по байтам" то есть с контролем состояния при чтении каждого байта из потока
Результат :
Идея неплоха, но из за буферизации данных ОС нормально не работает .

3 Запускать каждую операцию чтения в отдельном потоке с самостоятельным отлеживанием таймаута.
Примечание:
Для ускорения загрузки галереи картинок это это приходится делать в лбом случае ( последовательное чтение их списка просто "вымораживает" программу ) но там это происходит чуть иначе.
Результат :
Заметное улучшение контролируемости процесса но в то же время возникновение проблемы "современности " данных ( "зависший" процесс может внезапно "откопаться из сугроба" и начать запись данных по давно "убитому" адресу в памяти ) и просто современного завершения "повисшего процесса".
(Попытки остановить и завершить процесс принудительно
Типа такого
Код: Выделить всё
If (now-CTime)*10e4 >=OutTime then begin
LoadPicThread.Suspend;
While not LoadPicThread.Suspended do Application.ProcessMessages;;
LoadPicThread.Free;
  Exit;
end;

приводит к тяжелым сбоям)

Конкретные вопросы:
1 Есть ли надежные и быстрые способы проверки сосуществования файла или страницы без их чтения?
Мои идеи поэтому поводу довольно ущербны:
Код: Выделить всё
uses fphttpclient;
...
Var
ResponseCode:string;
Function TestURL_https(URL:String;Timeout,Attempts:Integer):Boolean;
Const httpclient: TFPHTTPClient=Nil;
var
RC,AT: integer;
S:string;
T:Byte;
Label L1;
begin
Result:=True;
At:=1;

httpclient := TFPHttpClient.Create(nil);
httpclient.IOTimeout:=Timeout;
try
SetProxy(httpclient,FSetup.ProxyEd.Text);
S:= httpclient.Options(Url);
  except
   Result:=False;
end;
  RC:= httpclient.ResponseStatusCode;
ResponseStatusText := httpclient.ResponseStatusText ;
If  httpclient.ResponseStatusText <>'OK' Then begin
   ResponseStatusText :='Нет доступа';
    Result:=False;
  end;
   httpclient.Free;
end;
if not Result and (at< Attempts ) then begin
AT:=AT+1; Goto L1;
end;
end;


и
с компонентом Инди (Indy10)
Код: Выделить всё
uses
.. IdHTTP, IdSSLOpenSS,;
...
var
ResponseCode:String;
Function URLCheck(URL:String;Pause:Integer; At:Byte):Boolean;
Var A:Byte;
Label L1;
begin
ResponseCode:= -100;
A:=1;
L1:
try
result:=true;
//IdHTTP1.ConnectTimeout:=100;
//IdHTTP1.ReadTimeout:=50;
IdHTTP1.HTTPOptions := IdHTTP1.HTTPOptions + [hoNoProtocolErrorException];
IdHTTP1.Head(URL);

result := IdHTTP1.Response.ResponseCode = 200;
ResponseCode:=IdHTTP1.Response.ResponseCode;
except
result:=False;
end;
If A<AT Then begin
Sleep(Pause);AT:=AT+1;
Goto L1;
end;
end;


2 Имеет ли в принципе смысл делать "Чтение потока из сети по байтам"?
Как-то так:
Код: Выделить всё
//  HttpGetBinary2  старая самоделка на основе synapse
ms:=TMemoryStream.Create;
If HttpGetBinary2(URL,'80', ms) then
begin
  ms.Seek(0, soFromBeginning);
  Tmp:=MS.ReadByte;// Читаю только один байт
  end ;
finally
   Result:=False;
    ms.Free;
end;
st.Free;


Как верно обустроить потоковое чтение данных при чтении ОДНОГО файла (нтмл кода или картинки ) из сети?

Мой временный "чит-код" выглядит довольно криво и не вызывает доверия даже у меня самого :
Код: Выделить всё
  Const
  T_End:Boolean=True;

Var
ffIM: Timage;
CTime:TDateTime;
// поток LoadPicThread_3
Type
TLoadPicThread_3=Class(TThread)
  private
  protected
    procedure Execute; override;
    procedure Load;
    procedure SLoad;
  public
   UPDATE  :Boolean;
    fIM: Timage;
    fURL:String;
    IsTO,IsInternet:Boolean;
    constructor Create(CreateSuspended: boolean;URL:
                            String;Var Im: Timage);

  end;
procedure TLoadPicThread_3.Load;
Var
B:TBitmap;
begin
try
   B:=RE_NetLoadBMP_PHP(fURL);// "Обычное" чтение картинки .

   if Not  FreeOnTerminate then begin
   if b.Modified then fIm.Picture.Bitmap.Assign(B);
  fIm.Picture.Bitmap.Modified:=b.Modified;
   end;

  B.Free;
  except
end;
end;

procedure TLoadPicThread_3.SLoad;
begin
IsInternet:= IsInternetConnected ;
end;

procedure  TLoadPicThread_3.Execute;
begin

while (not Terminated) do
If  UPDATE then begin
Synchronize(@SLoad);
if   IsInternet then  Load;
if  not  T_End then
T_End:=True;
UPDATE  :=False;
FreeOnTerminate :=  True;
if IsTO Then fIm.Free;
Terminate;
end;
end;

constructor  TLoadPicThread_3.Create(CreateSuspended: boolean;
                         URL:String;Var Im: Timage);
begin
  UPDATE  := False;
  fIM:=im;
  ffIM:=im;
  fUrl:=Url;

  T_End:= False;
  FreeOnTerminate :=  False;
  IsTO:=False;
  inherited Create(CreateSuspended);
end;


Type
TWProc=Procedure (Im: Timage);
Var
WProc:TWProc;
Timer_NTT:TLoadPicThread_3;
CTime:TDateTime;
FOutTime:TDateTime;

procedure Tform1.Timer4Timer(Sender: TObject);
begin
Timer4.Enabled:=False;
if not T_End then begin
If (now-CTime)*10e4 >=FOutTime then begin

Timer_NTT.IsTO:=True;
WProc(Nil);
  Exit;
end;
Timer4.Enabled:=True;
end else begin
WProc(ffIm);
ffIm.Free;
Timer_NTT.Terminate;
end;
end;


Сама функция чтения .
Код: Выделить всё

Function Thread_RE_NetLoadBMP_PHP(URL:String;OutTime:TDateTime=1.0;
                         PWProc:Pointer=Nil;
                         H:integer=0;W:integer=0;
                         PHP:Boolean=false):TBitmap;

var
NTT:TLoadPicThread_3;
IM:TImage;

begin
Result:=nil;
If not T_End then exit;
IM:=TImage.Create(Nil);
If PWProc<> NIL then
begin
If FSetup.Timer4.Enabled then exit;
WProc:=TWProc(  PWProc );
Timer_NTT:=TLoadPicThread_3.Create(True,{False} URL, Im);
Timer_NTT.Start;
CTime:=Now;Timer_NTT.UPDATE:=true;
FOutTime:=OutTime;
  FSetup.Timer4.Enabled:=True;
  Exit;
end;

NTT:=TLoadPicThread_3.Create(True,{False} URL, Im);
NTT.Start;
CTime:=Now;NTT.UPDATE:=true;
While not T_End do begin
If (now-CTime)*10e4 >=OutTime then begin
NTT.IsTO:=True;
  Exit;
end else NTT.Resume;
Application.ProcessMessages;
end;
Result:=TBitmap.Create;
Result.Assign(Im.Picture.Bitmap);
NTT.Terminate;
IM.Free;
end;

У нее есть два режима
1 Простой "с ожиданием"
BB:=Thread_RE_NetLoadBMP_PHP(S);
2 С "отложенным чтением" ( с проверкой по таймеру )
Код: Выделить всё
Procedure TestIMG (Im:TImage);far;
begin
...
If Im = Nil Then exit; // если таймаут
...
im.Free;
...
end;
...USetUp01.Thread_RE_NetLoadBMP_PHP(S,1.0,@TestIMG);


В общем понятно, что логика этой поделки малость хромает и есть множество возможностей "чему-то пойти не так", однако, пока лучше ничего не придумал .
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Как надежно "парировать" собой и тормоза при чтении из С

Сообщение Ichthyander » 22.10.2022 23:33:31

Длинный текст, подозреваю тут опять изобретается велосипед.
Вкратце на очевидный вопрос
1 Есть ли надежные и быстрые способы проверки сосуществования файла или страницы без их чтения?

Запрос
Код: Выделить всё
HEAD
вместо
Код: Выделить всё
GET
Аватара пользователя
Ichthyander
энтузиаст
 
Сообщения: 686
Зарегистрирован: 04.04.2007 08:32:43
Откуда: Астрахань

Re: Как надежно "парировать" собой и тормоза при чтении из С

Сообщение Alex2013 » 23.10.2022 18:00:37

Ichthyander писал(а):Длинный текст, подозреваю тут опять изобретается велосипед.
Вкратце на очевидный вопрос
1 Есть ли надежные и быстрые способы проверки сосуществования файла или страницы без их чтения?

Запрос
Код: Выделить всё
HEAD
вместо
Код: Выделить всё
GET


Это я уже пробовал !
Код: Выделить всё
IdHTTP1.HTTPOptions := IdHTTP1.HTTPOptions + [hoNoProtocolErrorException];
IdHTTP1.Head(URL);
result := IdHTTP1.Response.ResponseCode = 200;

( Подробности описаны выше )

Но как я писал раннее в дельфи работала более продвинутая версия.
(правда кажется без поддержки https)

Код: Выделить всё
uses wininet;

function CheckUrl(url: string): boolean;
var
  hSession, hfile, hRequest: hInternet;
  dwindex, dwcodelen: dword;
  dwcode: array [1..20] of char;
  res: pchar;
begin
  if pos('http://', lowercase(url)) = 0 then
    url := 'http://'+url;
  Result := false;
  hSession := InternetOpen('InetURL:/1.0', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if assigned(hsession) then
  begin
    hfile := InternetOpenUrl(hsession, pchar(url), nil, 0, INTERNET_FLAG_RELOAD, 0);
    dwIndex := 0;
    dwCodeLen := 10;
    HttpQueryInfo(hfile, HTTP_QUERY_STATUS_CODE, @dwcode, dwcodeLen, dwIndex);
    res := pchar(@dwcode);
    result := (res = '200') or (res = '302');
    if assigned(hfile) then
      InternetCloseHandle(hfile);
    InternetCloseHandle(hsession);
  end;
end;

Но честно говоря есть сомнения в отсутствии возможности "повисания" и в ней тоже.
Проблема ведь не в том что-бы просто проверить или получить ошибку по таймауту проблема в том, что есть ошибки ввода вывода при которых таймаут напрочь игнорируется и вообще вызывает программный сбой.
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44

Re: Как надежно "парировать" собой и тормоза при чтении из С

Сообщение Alex2013 » 29.10.2022 11:55:16

Кстати выше описанный CheckUrl заработал. Чего ему не хватало раньше шут его знает . ( Причем работает и с https )
Alex2013
долгожитель
 
Сообщения: 3049
Зарегистрирован: 03.04.2013 11:59:44


Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: Google [Bot] и гости: 23

Рейтинг@Mail.ru