![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
idHTTP при работе в потоке [D6, WinXP]
leonidus © (2005-03-06 13:01:00)Не понимаю почему компонент idHTTP крайне нестабильно работает в потоках (многие документы недокачиваются, получается куча документов размером окого килобайта). В связи с обсуждением сходных проблем с потоками в параллельной ветке в основных вопросах этого форума, вопрос: если компоненты idHTTP (у меня их четыре) лежат на форме, я что не могу запустить их работу в потоке, нужно их обязательно создавать в run-tim`е, в этом проблема ?
leonidus (2005-03-19 23:53:00) [1]>Alexander Panov, код ваш мучаю второй день - намучил уже много:)
Появился ряд вопросов:
1. Как остановить закачку всех потоков и выбранного?
2. Специально следил через диспетчер задач за тем как программа создает и убивает потоки - все корректно, всязи с чем вопрос, как поток узнает что поза вызвать дектруктор?
3. У idHTTP есть очень нужное мне событие OnWork, в нем удобно считать скорость закачки, отображать прогресс закачки, там же планирую ставить ограничение на скорость скачивания, как его прикрутить к созданному в рантайме компоненту? Я делаю так,
при объявлении класса добавляю procedure OnWork:
TGetHTTP=class
private
HTTP_component: TIdHTTP;
Log_component: TIdLogDebug;
FRecv,FSent: Integer;
BufRecv: String;
procedure LogReceive(ASender: TIdConnectionIntercept;
AStream: TStream);
procedure LogSend(ASender: TIdConnectionIntercept;
AStream: TStream);
procedure OnWork(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
...
...
в конструкторе TGetHTTP, добавляю строчку: HTTP_component.OnWork:=OnWork, получаем:
constructor TGetHTTP.Create;
begin
inherited Create;
HTTP_component := TIdHTTP.Create(nil);
HTTP_component.OnWork:=OnWork;
...
end;
и потом создаю саму процедуру:
procedure TGetHTTP.OnWork(Sender: TObject; AWorkMode: TWorkMode;
const AWorkCount: Integer);
begin
end;
но вот тут то и проблема, мне нужно передать потоку значение AWorkCount для того что бы знать сколько скачано байт, данным экземпляром класса и зная общий размер документа отобразить прогресс закачки, но как осуществить передачу этой информации не понятно совершенно.
4. Зачем нужен Log_component, какой от него прок?
5. Почему в вашем коде не используется idAntifrezee, или он нужен только если idHTTP лежат не форме?
6. У меня 9-я Indy, помню в 8-й ConnectionTimeout работал очень глючно, сейчас можно на него положиться? И как должен повести себя поток в котором истечет период ожидания?
Простите за такую массу вопросов, просто начал разбиратся, стало крайне интересно, я раньше особенно потоками не занимался, а тут еще и нелюбимый мной ООП во всей своей красе:)
leonidus (2005-03-18 16:35:00) [2]TThrHTTP=class(TThread)
private
FQ: TGetHTTP;
FQuery: String;
FLogControl: TMemo;
FMessage: String;
procedure Display;
protected
procedure Execute; override;
public
constructor Create(aQuery: String;aLogControl: TMemo);
destructor Destroy;override;
procedure Break;
end;
Alexander Panov (2005-03-18 15:48:00) [3]leonidus © (18.03.05 15:42) [23]
и кстали кк в Вашем коде получить скачанный файла на диске
TNetResult=record
ResCode: Integer; Код возврата
ResMsg: String; Сообщение об ошибке(напр. 200 OK)
ResHead: String; Полученный заголовок
ResBody: String; Полученная страница
Trafic: TTrafic; Объем трафика
end;
leonidus (2005-03-18 15:42:00) [4]>Alexander Panov, код работает, еще раз спасибо. но все таки хочется что бы можно было не создавать компоненты в run-time а использовать уже существующие, и кстали кк в Вашем коде получить скачанный файла на диске?
leonidus (2005-03-18 15:25:00) [5]щас попробую, но почему же мой код не работает?
leonidus (2005-03-18 15:24:00) [6]>Alexander Panov я в рантайме idHTTP не создаю, мне удобнее все настроить в design-time, а разве есть разница?
Alexander Panov (2005-03-18 15:21:00) [7]тогда вот пример на основе пред. класса:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdIntercept, IdLogBase, IdLogDebug, IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient, IdHTTP;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TTrafic=record
InBytes,OutBytes: Integer;
end;
TNetParm=record
Query: String;
UserName: String;
Password: String;
BasicAuth: Boolean;
ProxyAddr: String;
ProxyPort: Integer;
ProxyUserName: String;
ProxyPassword: String;
PostQuery: String;
Cookies: String; //Lines with #13#10
end;
TNetResult=record
ResCode: Integer;
ResMsg: String;
ResHead: String;
ResBody: String;
Trafic: TTrafic;
end;
TGetHTTP=class
private
IH: TIdHTTP;
IL: TIdLogDebug;
FRecv,FSent: Integer;
BufRecv: String;
procedure LogReceive(ASender: TIdConnectionIntercept;
AStream: TStream);
procedure LogSend(ASender: TIdConnectionIntercept;
AStream: TStream);
procedure SetParam(Parm: TNetParm);
public
constructor Create;
destructor Destroy; override;
function Get(Parm: TNetParm): TNetResult;
function Post(Parm: TNetParm): TNetResult;
end;
TThrHTTP=class(TThread)
private
FQ: TGetHTTP;
FQuery: String;
FLogControl: TMemo;
FMessage: String;
procedure Display;
protected
procedure Execute; override;
public
constructor Create(aQuery: String;aLogControl: TMemo);
destructor Destroy;override;
end;
var
Form1: TForm1;
HT: TThrHTTP;
implementation
{$R *.dfm}
{ TThrHTTP }
constructor TGetHTTP.Create;
begin
inherited Create;
IH := TIdHTTP.Create(nil);
IL := TIdLogDebug.Create(nil);
IH.Intercept := IL;
IL.OnReceive := LogReceive;
IL.OnSend := LogSend;
IL.Active := True;
end;
destructor TGetHTTP.Destroy;
begin
IL.Free;
IH.Free;
inherited;
end;
function TGetHTTP.Get(Parm: TNetParm): TNetResult;
begin
SetParam(Parm);
try
Result.ResBody := IH.Get(Parm.Query);
Result.ResHead := Copy(BufRecv,1,Pos(#13#10#13#10,BufRecv)-1);
except
end;
Result.ResCode := IH.ResponseCode;
Result.ResMsg := IH.ResponseText;
Result.Trafic.InBytes := FRecv;
Result.Trafic.OutBytes := FSent;
end;
procedure TGetHTTP.LogReceive(ASender: TIdConnectionIntercept;
AStream: TStream);
var
s: String;
begin
SetLength(s,AStream.Size);
AStream.Read(s[1],AStream.Size);
BufRecv := BufRecv + s;
FRecv := FRecv+AStream.Size;
end;
procedure TGetHTTP.LogSend(ASender: TIdConnectionIntercept;
AStream: TStream);
begin
FSent := FSent+AStream.Size;
end;
function TGetHTTP.Post(Parm: TNetParm): TNetResult;
var
tL: TStringList;
begin
SetParam(Parm);
tL := TStringList.Create;
try
tL.Text := Parm.PostQuery;
try
Result.ResBody := IH.Post(Parm.Query,tL);
Result.ResHead := Copy(BufRecv,1,Pos(#13#10#13#10,BufRecv)-1);
except
end;
finally
tL.Free;
end;
Result.ResCode := IH.ResponseCode;
Result.ResMsg := IH.ResponseText;
Result.Trafic.InBytes := FRecv;
Result.Trafic.OutBytes := FSent;
end;
procedure TGetHTTP.SetParam(Parm: TNetParm);
begin
IH.Request.Username := Parm.UserName;
IH.Request.Password := Parm.Password;
IH.Request.BasicAuthentication := Parm.BasicAuth;
IH.ProxyParams.ProxyPort := Parm.ProxyPort;
IH.ProxyParams.ProxyServer := Parm.ProxyAddr;
IH.ProxyParams.ProxyUsername := Parm.ProxyUserName;
IH.ProxyParams.ProxyPassword := Parm.ProxyPassword;
IH.Request.CustomHeaders.Text := Parm.Cookies;
IH.Request.UserAgent := 'IE?';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TThrHTTP.Create(' http://www.lib.ru/',Memo1);
TThrHTTP.Create(' http://www.lib.ru/',Memo1);
TThrHTTP.Create(' http://www.lib.ru/',Memo1);
TThrHTTP.Create(' http://www.lib.ru/',Memo1);
end;
{ TThrHTTP }
constructor TThrHTTP.Create(aQuery: String;aLogControl: TMemo);
begin
inherited Create(True);
FreeOnTerminate := True;
FQ := TGetHTTP.Create;
FQuery := aQuery;
FLogControl := aLogControl;
Resume;
end;
destructor TThrHTTP.Destroy;
begin
FQ.Free;
inherited;
end;
procedure TThrHTTP.Display;
begin
FLogControl.Lines.Add(FMessage);
end;
procedure TThrHTTP.Execute;
var
NP: TNetParm;
NR: TNetResult;
begin
FMessage := 'Старт потока';
Synchronize(Display);
NP.Query := FQuery;
NR := FQ.Get(NP);
FMessage := 'Запрос выполнен';
Synchronize(Display);
end;
end.
leonidus (2005-03-18 15:17:00) [8]> Alexander Panov спасибо за код, сейчас посмотрю, однако подправил свой код с учетом синхронизации, получилось вот что (это для одного потока, остальные подобный):
TThread1 = class(TThread)
private
protected
procedure Execute; override;
procedure showlog;
public
URL: string;
log: string;
end;
procedure TThread1.showlog;
begin
form1.memo2.lines.add(t1.log);
end;
procedure TThread1.Execute;
var
f_stream:tfilestream;
error:boolean;
begin
t1.log:='1-й поток запущен';
synchronize(showlog);
f_stream:=tfilestream.Create('D:\WebTransporter\Exp\idHTTP\download\1.html',fmCreate);
//Пытаемся получить http-документ
try
form1.HTTP1.Get(t1.url,f_stream);
except
on E: Exception do error:=true;
end;
//ошибка в потоке после оператора Get
if error then
begin
try
form1.HTTP1.DisconnectSocket;
finally
f_stream.Free;
end;
t1.log:='ошибка в 1-м потоке';
synchronize(showlog);
end
else f_stream.Free;
end;
В результате в логе:
1-й поток запущен
2-й поток запущен
3-й поток запущен
4-й поток запущен
ошибка в 1-м потоке
ошибка во 2-м потоке
ошибка в 4-м потоке
ошибка в 3-м потоке
в общем-то без разницы:(
Alexander Panov (2005-03-18 14:57:00) [9]1. Почитай статьи на сайте по работе с потоками, примеры в демках посмотри.
А вот пример создания в ран-тайме запроса.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdIntercept, IdLogBase, IdLogDebug, IdBaseComponent,
IdComponent, IdTCPConnection, IdTCPClient, IdHTTP;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TTrafic=record
InBytes,OutBytes: Integer;
end;
TNetParm=record
Query: String;
UserName: String;
Password: String;
BasicAuth: Boolean;
ProxyAddr: String;
ProxyPort: Integer;
ProxyUserName: String;
ProxyPassword: String;
PostQuery: String;
Cookies: String; //Lines with #13#10
end;
TNetResult=record
ResCode: Integer;
ResMsg: String;
ResHead: String;
ResBody: String;
Trafic: TTrafic;
end;
TGetHTTP=class
private
IH: TIdHTTP;
IL: TIdLogDebug;
FRecv,FSent: Integer;
BufRecv: String;
procedure LogReceive(ASender: TIdConnectionIntercept;
AStream: TStream);
procedure LogSend(ASender: TIdConnectionIntercept;
AStream: TStream);
procedure SetParam(Parm: TNetParm);
public
constructor Create;
destructor Destroy; override;
function Get(Parm: TNetParm): TNetResult;
function Post(Parm: TNetParm): TNetResult;
end;
var
Form1: TForm1;
HT: TGetHTTP;
implementation
{$R *.dfm}
{ TThrHTTP }
constructor TGetHTTP.Create;
begin
inherited Create;
IH := TIdHTTP.Create(nil);
IL := TIdLogDebug.Create(nil);
IH.Intercept := IL;
IL.OnReceive := LogReceive;
IL.OnSend := LogSend;
IL.Active := True;
end;
destructor TGetHTTP.Destroy;
begin
IL.Free;
IH.Free;
inherited;
end;
function TGetHTTP.Get(Parm: TNetParm): TNetResult;
begin
SetParam(Parm);
try
Result.ResBody := IH.Get(Parm.Query);
Result.ResHead := Copy(BufRecv,1,Pos(#13#10#13#10,BufRecv)-1);
except
end;
Result.ResCode := IH.ResponseCode;
Result.ResMsg := IH.ResponseText;
Result.Trafic.InBytes := FRecv;
Result.Trafic.OutBytes := FSent;
end;
procedure TGetHTTP.LogReceive(ASender: TIdConnectionIntercept;
AStream: TStream);
var
s: String;
begin
SetLength(s,AStream.Size);
AStream.Read(s[1],AStream.Size);
BufRecv := BufRecv + s;
FRecv := FRecv+AStream.Size;
end;
procedure TGetHTTP.LogSend(ASender: TIdConnectionIntercept;
AStream: TStream);
begin
FSent := FSent+AStream.Size;
end;
function TGetHTTP.Post(Parm: TNetParm): TNetResult;
var
tL: TStringList;
begin
SetParam(Parm);
tL := TStringList.Create;
try
tL.Text := Parm.PostQuery;
try
Result.ResBody := IH.Post(Parm.Query,tL);
Result.ResHead := Copy(BufRecv,1,Pos(#13#10#13#10,BufRecv)-1);
except
end;
finally
tL.Free;
end;
Result.ResCode := IH.ResponseCode;
Result.ResMsg := IH.ResponseText;
Result.Trafic.InBytes := FRecv;
Result.Trafic.OutBytes := FSent;
end;
procedure TGetHTTP.SetParam(Parm: TNetParm);
begin
IH.Request.Username := Parm.UserName;
IH.Request.Password := Parm.Password;
IH.Request.BasicAuthentication := Parm.BasicAuth;
IH.ProxyParams.ProxyPort := Parm.ProxyPort;
IH.ProxyParams.ProxyServer := Parm.ProxyAddr;
IH.ProxyParams.ProxyUsername := Parm.ProxyUserName;
IH.ProxyParams.ProxyPassword := Parm.ProxyPassword;
IH.Request.CustomHeaders.Text := Parm.Cookies;
IH.Request.UserAgent := 'IE?';
end;
procedure TForm1.Button1Click(Sender: TObject);
var
parm: TNetParm;
Res: TNetResult;
begin
HT := TGetHTTP.Create;
Parm.Query := Edit1.Text;
Res := HT.Get(parm);
Memo1.Lines.Text := Res.ResBody;
HT.Free;
end;
end.
leonidus (2005-03-18 14:47:00) [10]ну да, намек я понял, только что-то с synchronize не пойму что за трабла, пишу:
....
TThread1 = class(TThread)
private
protected
procedure Execute; override;
procedure showlog(st:string);
public
URL: string;
end;
....
procedure TThread1.showlog(st:string);
begin
form1.memo2.lines.add(st);
end;
procedure TThread1.Execute;
var
f_stream:tfilestream;
error:boolean;
begin
synchronize(showlog('1-й поток запущен'));
f_stream:=tfilestream.Create('D:\WebTransporter\Exp\idHTTP\download\1.html',fmCreate);
...
компилятор ругается на строчку synchronize(showlog('1-й поток запущен'));, прямое указание TThread1.synchronize(showlog('1-й поток запущен')); тоже не катит.
Digitman (2005-03-18 14:08:00) [11]
> procedure TThread1.Execute;
> ...
> form1.memo2.lines.add('1-й поток запущен');
ну и какого ляда ты обращаешься в VCL-объекту в доп.трэде ?
грешишь на TIdHTTP, а до него и дело-то вообще не доходит - лажа видна уже при обращении к обычному TMemo ...
leonidus (2005-03-18 13:42:00) [12]Написал небольшую тестовую программу реализующую 4-х поточную закачку документов компонентами idHTTP. На форме лежит 4 компонента idHTTP: HTTP1,HTTP2,HTTP3,HTTP4, компонент IdAntiFreeze1, два Memo, в первом 4 строки заполнено ссылками которые качаем:
http://www.lib.ru/
http://www.lib.ru/PROZA/
http://www.lib.ru/INPROZ/
http://www.lib.ru/RUFANT/
второй Memo будет содержать лог работы программы, также четыре Edit`а для установки параметров прокси-сервера и кнопка Button1 - "Старт".
Вот код:
TThread1 = class(TThread)
private
protected
procedure Execute; override;
public
URL: string;
end;
TThread2 = class(TThread)
private
protected
procedure Execute; override;
public
URL: string;
end;
TThread3 = class(TThread)
private
protected
procedure Execute; override;
public
URL: string;
end;
TThread4 = class(TThread)
private
protected
procedure Execute; override;
public
URL: string;
end;
var
t1:TThread1;
t2:TThread2;
t3:TThread3;
t4:TThread4;
begin
procedure TThread1.Execute;
var
f_stream:tfilestream;
error:boolean;
begin
form1.memo2.lines.add('1-й поток запущен');
f_stream:=tfilestream.Create('D:\WebTransporter\Exp\idHTTP\download\1.html',fmCreate);
try
form1.HTTP1.Get(t1.url,f_stream);
except
on E: Exception do error:=true;
end;
if error then
begin
try
form1.HTTP1.DisconnectSocket;
finally
f_stream.Free;
end;
form1.memo2.lines.add('ошибка в 1-м потоке');
end
else f_stream.Free;
end;
procedure TThread2.Execute;
var
f_stream:tfilestream;
error:boolean;
begin
form1.memo2.lines.add('2-й поток запущен');
f_stream:=tfilestream.Create('D:\WebTransporter\Exp\idHTTP\download\2.html',fmCreate);
try
form1.HTTP2.Get(t2.url,f_stream);
except
on E: Exception do error:=true;
end;
if error then
begin
try
form1.HTTP2.DisconnectSocket;
finally
f_stream.Free;
end;
form1.memo2.lines.add('ошибка во 2-м потоке');
end
else f_stream.Free;
end;
procedure TThread3.Execute;
var
f_stream:tfilestream;
error:boolean;
begin
form1.memo2.lines.add('3-й поток запущен');
f_stream:=tfilestream.Create('D:\WebTransporter\Exp\idHTTP\download\3.html',fmCreate);
try
form1.HTTP3.Get(t3.url,f_stream);
except
on E: Exception do error:=true;
end;
if error then
begin
try
form1.HTTP3.DisconnectSocket;
finally
f_stream.Free;
end;
form1.memo2.lines.add('ошибка во 3-м потоке');
end
else f_stream.Free;
end;
procedure TThread4.Execute;
var
f_stream:tfilestream;
error:boolean;
begin
form1.memo2.lines.add('4-й поток запущен');
f_stream:=tfilestream.Create('D:\WebTransporter\Exp\idHTTP\download\4.html',fmCreate);
try
form1.HTTP4.Get(t4.url,f_stream);
except
on E: Exception do error:=true;
end;
if error then
begin
try
form1.HTTP4.DisconnectSocket;
finally
f_stream.Free;
end;
form1.memo2.lines.add('ошибка во 4-м потоке');
end
else f_stream.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
//запускаем закачку
begin
http1.ProxyParams.ProxyServer:=Edit3.Text;
http1.ProxyParams.ProxyPort:=strtoint(Edit4.Text);
http1.ProxyParams.ProxyUsername:=edit1.Text;
http1.ProxyParams.ProxyPassword:=edit2.Text;
http2.ProxyParams.ProxyServer:=Edit3.Text;
http2.ProxyParams.ProxyPort:=strtoint(Edit4.Text);
http2.ProxyParams.ProxyUsername:=edit1.Text;
http2.ProxyParams.ProxyPassword:=edit2.Text;
http3.ProxyParams.ProxyServer:=Edit3.Text;
http3.ProxyParams.ProxyPort:=strtoint(Edit4.Text);
http3.ProxyParams.ProxyUsername:=edit1.Text;
http3.ProxyParams.ProxyPassword:=edit2.Text;
http4.ProxyParams.ProxyServer:=Edit3.Text;
http4.ProxyParams.ProxyPort:=strtoint(Edit4.Text);
http4.ProxyParams.ProxyUsername:=edit1.Text;
http4.ProxyParams.ProxyPassword:=edit2.Text;
t1 := TThread1.Create(true);
t1.URL :=memo1.Lines.Strings[0];
t1.Resume;
t2 := TThread2.Create(true);
t2.URL :=memo1.Lines.Strings[1];
t2.Resume;
t3 := TThread3.Create(true);
t3.URL :=memo1.Lines.Strings[2];
t3.Resume;
t4 := TThread4.Create(true);
t4.URL :=memo1.Lines.Strings[3];
t4.Resume;
end;
В результате выкачиваются все файлы... но в логе получаю:
2-й поток запущен
4-й поток запущен
3-й поток запущен
1-й поток запущен
ошибка во 2-м потоке
ошибка в 1-м потоке
ошибка в 3-м потоке
ошибка в 4-м потоке
Но, при этом все 4 документа скачиваются нормально, совершенно полностью. в чем же дело, если все нормально, то почему срабатывает исключительная ситуация в блоках типа:
try
form1.HTTP1.Get(t1.url,f_stream);
except
on E: Exception do error:=true;
end;
???
Все тесттровалось из локальной сети с доступом в инет через прокси-сервер.
leonidus (2005-03-11 08:56:00) [13]Пожалуйста если кто делал многопоточне приложения с idHTTP откликнитесь в приват.
Zelius (2005-03-09 19:32:00) [14]Я с ним не работал, а сейчас посмотрел - вроде все с ним в порядке в многоптоочности.
Все же, может там ошибка какая в потоке присходит?
leonidus (2005-03-09 13:55:00) [15]Антифриз установлен, кстати у него свойстов IdleTimeOut у меня установлено в 10, там вообще что как настраивается?
Reindeer Moss Eater (2005-03-09 12:43:00) [16]Антифиз (или его отсутствие) каким образом может быть причиной описаной проблемы?
Zelius (2005-03-09 12:25:00) [17]Хотя, есть нюанс, используешь ли ты AntiFreeze?
Reindeer Moss Eater (2005-03-09 10:42:00) [18]Программирование TidHTTP в потоках ничем не отличается от такого же не в потоках (тем более, что все равно главный поток - это тот же самый поток, только основной).
У компонента "чудесным образом" не "вырастают" новые методы и свойства если он используется во вторичном потоке.
Если же вопрос касается вторичных потоков вообще, то при чем здесь конференция "Сети"?
leonidus (2005-03-09 08:24:00) [19]>Mim2 а можно маленький примерик, хотя бы с двумя потоками?
Reindeer Moss Eater (2005-03-08 03:20:00) [20]Поэтому я и грешу на поточность, может тут какие-то хитрости есть именно с компонентами Инди?
Лучше грешить на свой код. Полезнее.