![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Винет поток на функции Socket(). [D7, WinXP]
DVM © (2007-06-04 11:02:00)Ситуация такая:
Есть кнопка, при нажатии на которую создается поток, выполняющий HTTP запрос и самоликвидирующийся после выполнения. Все вобщем работает нормально, если кнопку нажимать не очень часто. Если кнопку нажимать очень часто, то спустя некоторое время (100-150 нажатий), потоки продолжают создаваться, но уже не завершаются, т.к. виснут на вызове функции Socket(). В чем может быть проблема?
Вторичные потоки никак не взаиможействуют с главным после запуска, все параметры передаются в них при старте. Друг с другом тоже не взаимодействуют.
DVM (2007-06-04 13:42:00) [21]
> Есть же неблок.режим с асинхронными нотификациями
Я привык к блокирущим сокетам. Неблокирующий режим немного недолюбливаю и потому не использую без особой нужды. Хотя, наверное, я не прав.
Сергей М. (2007-06-04 13:35:00) [22]
> подумал, зачем так часто опрашивать select()
А действительно, зачем опрашивать вообще?
Есть же неблок.режим с асинхронными нотификациями (WSAAsyncSelect, WSAEventSelect) ..
DVM (2007-06-04 13:25:00) [23]
> Странная у тебя методика отладки) ...
>
> Дилетантством отлает .. А вроде бы ты и не новичок, чтобы
> бипами контролировать работу своего кода ..
Да я не всегда бипами. Просто так оно быстрее писать иногда, а эффект тот же. :) Если бип прошел или не прошел, тогда уж по обстоятельствам смотрю. Может это и дилетантство.
DVM (2007-06-04 13:22:00) [24]
> Если торопишься, то зачем тогда delay(50) ? Это же лишняя
> бестолковая задержка !
Может и лишняя строка, у меня у самого сомнения были на счет нее. Просто подумал, зачем так часто опрашивать select() - грузить систему. 50 мсек - это же совсем мало.
> Если узел недоступен, то даже при самой плохой связи результат
> попытки коннекта вряд ли задержится на такое ощутимое время.
>
> Обычно такие большие задержки связаны с отказами маршрутизации,
> а не с доступностью целевого узла.
Допустим я посылаю запрос узлу 192.168.0.20, собственный адрес 192.168.0.10 - connect() блокирует поток на 20 секунд где то. Попробуйте сами - положите на форму компонент от инди и кнопку и выполните запрос:
IdHTTP1.Get(' http://192.168.0.21');
Если узла такого нет, то у меня блокировка на 20 секунд возникает
Сергей М. (2007-06-04 13:18:00) [25]
> пробовал вчера делать так:
>
> try
> FSock := SocketConnect();
> except
> Windows.Beep(...);
> end;
Странная у тебя методика отладки) ...
Дилетантством отлает .. А вроде бы ты и не новичок, чтобы бипами контролировать работу своего кода ..
Сергей М. (2007-06-04 13:16:00) [26]
> А это долго (секунд 10-15)
А ты куда-то торопишься ?
Если торопишься, то зачем тогда delay(50) ? Это же лишняя бестолковая задержка !
> например когда узел недоступен
Если узел недоступен, то даже при самой плохой связи результат попытки коннекта вряд ли задержится на такое ощутимое время.
Обычно такие большие задержки связаны с отказами маршрутизации, а не с доступностью целевого узла.
DVM (2007-06-04 13:14:00) [27]Кстати, по поводу
> ..
> try
> FSock := SocketConnect();
> except
> on e: Exception do begin
> MessageBox(0, PChar(e.Classname + ' ' + e.Message), '',
> MB_OK or MB_SETFOREGROUND);
> raise;
> end;
> end;
> ..
:
Я пробовал вчера делать так:
try
FSock := SocketConnect();
except
Windows.Beep(...);
end;
Тишина была.
DVM (2007-06-04 13:08:00) [28]
> 1. Зачем вызывать SocketDisconnect() безусловно, если предшествующий
> SocketConnect() может вернуть отказ ?
Согласен
> 2. Зачем возвращать гнездо в блок.режим перед его закрытием
> ?
Согласен, правда этого куска раньше не было, я в поисках ошибки вставил его и потом забыл.
> 3. Зачем нужен неблокирующий коннект, если все остальные
> операции с гнездом - блокирующие ?
А вот зачем. Если поток висит на Connect() в случае блокирующих сокетов (например когда узел недоступен), то завершение такого потока невозможно до того как выйдет таймаут. А это долго (секунд 10-15). Чтобы быстро прервать Connect() я и превожу сокеты в неблокирующий режим. Далее работаю с блокирующими, т.к. это проще.
Сергей М. (2007-06-04 12:59:00) [29]1. Зачем вызывать SocketDisconnect() безусловно, если предшествующий SocketConnect() может вернуть отказ ?
2. Зачем возвращать гнездо в блок.режим перед его закрытием ?
3. Зачем нужен неблокирующий коннект, если все остальные операции с гнездом - блокирующие ?
DVM (2007-06-04 12:42:00) [30]
> Сергей М. © (04.06.07 12:29) [8]
Странно, но даже с моим кодом описанный эффект пропал на другом компьютере. Проявлялся ранее на домашнем компьютере, на рабочем не могу добиться того же. Тогда дома попробую.
> Куча серьезных замечаний по коду.
Каких?
Сергей М. (2007-06-04 12:29:00) [31]Куча серьезных замечаний по коду.
Ну да это не столь важно пока.
Пробуй вот такой код:
..
try
FSock := SocketConnect();
except
on e: Exception do begin
MessageBox(0, PChar(e.Classname + ' ' + e.Message), '', MB_OK or MB_SETFOREGROUND);
raise;
end;
end;
..
Изменения в поведении программы при этом наблюдаются ?
DVM (2007-06-04 12:16:00) [32]
> Какой резон использовать VCL (хотя бы в части задействования
> класса TThread), если работа с Winsock организована непосредственно
> на WinsockAPI ?
Да собственно, особенного резона, конечно нет. С оберткой немного удобнее.
> Как определил ?
Элементарно поставил Windows.Beep(500,50); сначала до, потом после строки этой (Socket()) - до всегда пикает, после с некоторого момента пикать перестает.
> Приводи код TThread ..
Это метод Execute:
procedure TCommandThread.Execute;
var
Wsa: TWSADATA;
WsaErr: integer;
Res: integer;
begin
FreeOnTerminate := true;
WsaErr := WSAStartUp($0101, Wsa);
try
if WsaErr = 0 then
begin
Init;
FSock := SocketConnect();
try
if FSock <> -1 then
begin
Res := SendRequest(FSock, FRequest);
if Res = 0 then ReadData(FBuffer, 0);
end
finally
SocketDisconnect();
end;
end;
finally
WSACleanUp;
end;
end;
Это то место где коннект:
function TCommandThread.SocketConnect: integer;
var
Len: integer;
Block: Cardinal;
Wfd: TFDSet;
TimeVal: TTimeVal;
BeginConnectTime: Cardinal;
begin
Result := socket(AF_INET, SOCK_STREAM, 0);
if Result = INVALID_SOCKET then
begin
Result := -1;
exit;
end;
Block := 1;
if ioctlsocket(Result, FIONBIO, Block) = SOCKET_ERROR then
begin
CloseSocket(Result);
Result := -1;
exit;
end;
Len := SizeOf(FAddr);
TimeVal.tv_sec := 0;
TimeVal.tv_usec := 100;
FD_ZERO(wfd);
FD_SET(result, wfd);
if Connect(Result, @FAddr, Len) = SOCKET_ERROR then
begin
if WSAGetLastError = WSAEWOULDBLOCK then
begin
BeginConnectTime := GetTickCount;
while (not Terminated) and ((GetTickCount - BeginConnectTime) <= 5000) do
begin
case select(Result, nil, @wfd, nil, @TimeVal) of
0:
begin
FD_ZERO(wfd);
FD_SET(result, wfd);
delay(50);
end;
1:
if FD_ISSET(Result, wfd) then break;
SOCKET_ERROR:
begin
Block := 0;
ioctlsocket(Result, FIONBIO, Block);
CloseSocket(Result);
Result := -1;
end;
end;
end
end
else
begin
CloseSocket(Result);
Result := -1;
end;
end;
Block := 0;
if ioctlsocket(Result, FIONBIO, Block) = SOCKET_ERROR then
begin
CloseSocket(Result);
Result := -1;
exit;
end;
end;
Сергей М. (2007-06-04 12:07:00) [33]
> используется именно TThread
Какой резон использовать VCL (хотя бы в части задействования класса TThread), если работа с Winsock организована непосредственно на WinsockAPI ?
Поясни ..
> выполнение останавливается на Socket
Как определил ?
Приводи код TThread ..
DVM (2007-06-04 12:02:00) [34]
> Подозреваю, что речь не идет о TThread.
Нет здесь используется именно TThread c флагом FreeOnTerminate=true
Но дело не в потоке. Его выполнение останавливается на Socket()
Сергей М. (2007-06-04 11:46:00) [35]
> SpellCaster (04.06.07 11:40) [2]
А причем здесь это ?
Подозреваю, что речь не идет о TThread.
А в этом случае автор должен сам позаботиться о закрытии хэндлов.
clickmaker (2007-06-04 11:42:00) [36]
> [2] SpellCaster (04.06.07 11:40)
procedure TThread.Terminate;
begin
FTerminated := True;
end;
;)
SpellCaster (2007-06-04 11:40:00) [37]> Поток не может "самоликвидироваться".
А как же вызов Terminate в методе Execute с FreeOnTerminate=False?
Сергей М. (2007-06-04 11:21:00) [38]
> поток..самоликвидирующийся после выполнения
Поток не может "самоликвидироваться".
"Ликвидация" объекта-нити - это закрытие всех хэндлов, ассоциированных с этим объектом.