![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Винет поток на функции Socket(). [D7, WinXP]
DVM © (2007-06-04 11:02:00)Ситуация такая:
Есть кнопка, при нажатии на которую создается поток, выполняющий HTTP запрос и самоликвидирующийся после выполнения. Все вобщем работает нормально, если кнопку нажимать не очень часто. Если кнопку нажимать очень часто, то спустя некоторое время (100-150 нажатий), потоки продолжают создаваться, но уже не завершаются, т.к. виснут на вызове функции Socket(). В чем может быть проблема?
Вторичные потоки никак не взаиможействуют с главным после запуска, все параметры передаются в них при старте. Друг с другом тоже не взаимодействуют.
Сергей М. (2007-06-06 08:17:00) [1]
> DVM © (05.06.07 18:16) [37]
>
>
> Чего сказать то хотел? Я все это знаю не хуже тебя
А раз знаешь, то к чему упоминать "железность" сервера ? Чего сказать-то хотел этим ?
> ты сам противоречишь себе в постах 35 и 36.
Да неужели ?
DVM (2007-06-05 18:16:00) [2]
> Сергей М. © (05.06.07 16:16) [35]
Набор прописных истин. Чего сказать то хотел? Я все это знаю не хуже тебя.
> Читать вовсе не обязательно, а вот рвать соединение с сервером
> сразу же после успешной отправки запроса как раз и может
> быть чревато отменой выполнения запроса - многие серверы
> (и не только вэб-, но и к примеру sql-серверы) в момент
> исполнения запроса отслеживают статус соединения с клиентом
> и прерывают исполнение запроса в случае обнаружения разрыва
> соединения (аварийного или по инициативе клиента), дабы
> не держать занятыми ставшие при этом ненужными ресурсы.
А это мысль. Т.е просто подождать некоторое время после запроса надо.
Попробую, за это спасибо.
Только ты сам противоречишь себе в постах 35 и 36.
Из 35 следует, что сервер выполнит запрос если сам запрос верен. Т.е правильный запрос - это достаточное условие.
В 36 ты вводишь еще утверждение, что оказвается еще не надо сразу рвать соединение. Т.е 35 недостаточное условие.
Сергей М. (2007-06-05 16:49:00) [3]
> DVM © (05.06.07 15:30) [34]
Читать вовсе не обязательно, а вот рватьсоединение с сервером сразу же после успешной отправки запроса как раз и может быть чревато отменой выполнения запроса - многие серверы (и не только вэб-, но и к примеру sql-серверы) в момент исполнения запроса отслеживают статус соединения с клиентом и прерывают исполнение запроса в случае обнаружения разрыва соединения (аварийного или по инициативе клиента), дабы не держать занятыми ставшие при этом ненужными ресурсы.
Так что чтение или нечтение тут совершенно ни при чем - ты послал запрос и тут же закрыл гнездо, и твой "железный" сервер, видимо, тут же прерывает выполнение запроса, т.е. до "щелкания реле" дело попросту не доходит.
Сергей М. (2007-06-05 16:16:00) [4]"Железный" вэб-сервер абсолютно ничемне отличается от любого другого вэб-сервера, хоть "деревянного", хоть "стеклянного".
Вэб-сервер он и в Африке вэб-сервер - получив полный и корректный вэб-запрос он обязанего немедленно выполнить, внезависимости от того чем занят клиент во время выполнения запроса.
> там реле переключиться должно
Если оно должно переключиться в ходе исполнениязапроса , но не переключилось, то это означает только одно - сервер не получил полный и/или корректный запрос. Все ! Остальное - досужие домыслы.
DVM (2007-06-05 15:30:00) [5]Я раньше не читал ответ, т.к. понимал, что по логике это вроде бы не требуется, но потом наткнулся на эту железяку с этим вэб-сервером и пришлось пересмотреть отношение к чтению ответа.
DVM (2007-06-05 15:26:00) [6]
> Ерунда полнейшая.
>
> Ни один сервер не может знать, чем занят его клиент в момент
> готовности исполнить поступившую от клиента команду.
>
> Если команда поступила, то на то она и поступила, чтобы
> ее исполнять.
Да я понимаю, но это факт проверенный. Почему так происходит, я не знаю - сервер встроен в некий девайс, что внутри - не знаю.
Но происходит именно так - делаю запрос к этой железяке, если я уберу из кода строку if Res = 0 then ReadData(FBuffer, 0); - то запрос вообще не выполняется (там реле переключиться должно), с этой же строкой - все нормально - запрос исполняется.
Я сам не понимаю как такое может происходить, но происходит же.
Сергей М. (2007-06-05 15:23:00) [7]
> не все сервера с которыми я работаю исполняют команду, если
> клиент после запроса не начинает читать ответ сервера
Ерунда полнейшая.
Ни один сервер не может знать, чем занят его клиент в момент готовности исполнить поступившую от клиента команду.
Если команда поступила, то на то она и поступила, чтобы ее исполнять.
DVM (2007-06-05 14:50:00) [8]
> А ты контролировал содержимое FAddr в момент исключения
> ?
> Нет, не контролировал.
Нет не контролировал. Но проверить смогу теперь только дома, т.к. глюк проявляется только там.
DVM (2007-06-05 14:49:00) [9]
> После успешного вызова ioctlsocket переменная TotalBytesToRead
> может содержать любое значение в диапазоне [0.. полный размер
> данных ответа на запрос].
> Задумайся над этим.
Я знаю. Ответ приходит порциями, естественно. Назначение функции ReadData() - считать очередную порцию, а не весь ответ сервера. Вообще эта функция должна вызываться в цикле, до тех пор пока не вернет 0 (нет больше данных для чтения).
В данном же конкретном коде мне ответ сервера вообще нафик не нужен, мне ему команду отдать надо, но дело в том, что не все сервера с которыми я работаю исполняют команду, если клиент после запроса не начинает читать ответ сервера. Эта функция перенесена из другого проекта без изменений, где она применялась правильно.
Сергей М. (2007-06-05 13:26:00) [10]
> Да я вроде FAddr не касаюсь больше нигде
А ты контролировал содержимое FAddr в момент исключения ?
Нет, не контролировал.
>if ioctlsocket(FSock, FIONREAD, TotalBytesToRead) = SOCKET_ERROR then
После успешного вызова ioctlsocket переменная TotalBytesToRead может содержать любоезначение в диапазоне [0.. полный размер данных ответа на запрос].
Задумайся над этим.
DVM (2007-06-05 13:15:00) [11]Да, и ведь глюк появляется на домашнем компьютере исключительно. На рабочем не появляется. Может с системой что не так?
DVM (2007-06-05 13:09:00) [12]
> Сергей М. © (05.06.07 12:57) [26]
Да я вроде FAddr не касаюсь больше нигде.
function TCommandThread.SendRequest(ASock: integer; ARequest: string): integer;
var
ReturnCode: integer;
begin
ReturnCode := send(ASock, Pointer(ARequest)^, Length(ARequest), 0);
if ReturnCode < 0 then
begin
Result := -1;
SocketDisconnect();
end
else
begin
Result := 0;
end;
end;
function TCommandThread.ReadData(ABuffer: TBuffer; BytesExpected: integer): integer;
const
MaxLen = 262144;
var
TotalBytesToRead, Found, TotalBytesRead, BytesToRead, BytesRead: integer;
Rfds: TFDSet;
TempBuff: array [0..Pred(MaxLen)] of Char;
begin
FD_ZERO(Rfds);
FD_SET(FSock, Rfds);
Found := select(FSock, @Rfds, nil, nil, @FTimeout);
if Found = 0 then
begin
Result := -1;
exit;
end
else
if Found = SOCKET_ERROR then
begin
Result := -1;
exit;
end;
TotalBytesToRead := 0;
if BytesExpected <> 0 then
begin
TotalBytesToRead := BytesExpected;
end
else
begin
if ioctlsocket(FSock, FIONREAD, TotalBytesToRead) = SOCKET_ERROR then
begin
Result := -1;
exit;
end;
if TotalBytesToRead = 0 then
begin
SocketDisconnect();
Result := 0;
exit;
end;
end;
TotalBytesRead := 0;
repeat
if TotalBytesToRead > MaxLen then
BytesToRead := MaxLen
else
BytesToRead := TotalBytesToRead;
ZeroMemory(@TempBuff[0], MaxLen);
BytesRead := recv(FSock, TempBuff, BytesToRead, 0);
if BytesRead = SOCKET_ERROR then
begin
SocketDisconnect();
Result := -1;
exit;
end
else
if BytesRead = 0 then
begin
SocketDisconnect();
Result := 0;
exit;
end
else
begin
if ABuffer.Size >= 2097152 then
begin
SocketDisconnect();
Result := -1;
exit;
end;
ABuffer.Append(@TempBuff[0], BytesRead);
TotalBytesRead := TotalBytesRead + BytesRead;
TotalBytesToRead := TotalBytesToRead - BytesRead;
end;
until (TotalBytesToRead <= 0) or Terminated;
Result := TotalBytesRead;
end;
Сергей М. (2007-06-05 12:57:00) [13]"Криминал" вполне может твориться в SendRequest и ReadData
Сергей М. (2007-06-05 12:55:00) [14]
> мне что-то непонятна причина его возникновения
Похоже где-то ты гадишь содержимое переменной FAddr
DVM (2007-06-05 12:46:00) [15]
> И что же это за исключение ?
EAccessViolation AccessViolation at address 7c80979d in module kernel32.dll. Write of address 00c35298.
Только мне что-то непонятна причина его возникновения.
Сергей М. (2007-06-05 11:05:00) [16]И что же это за исключение ?
DVM (2007-06-04 23:36:00) [17]Двигая try...except по коду - выявил меcто исключения:
function TCommandThread.SocketConnect: integer;
var
Len: integer;
Block: Integer;
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);
try
Connect(Result, fAddr, Len) ; ///////// тут!
except
on e: Exception do
begin
MessageBox(0, PChar(e.Classname + ' ' + e.Message), '', MB_OK or MB_SETFOREGROUND);
raise;
end;
end;
// 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;
DVM (2007-06-04 23:26:00) [18]
> Сергей М. © (04.06.07 12:29) [8]
Последовал Вашему совету, вставил указанный код. На домашнем компьютере вылазит следующее:
EAccessViolation AccessViolation at address 7c80979d in module kernel32.dll. Write of address 00c35298.
Что это может быть? На рабочем компьютере так и не добился этой ошибки.
Сергей М. (2007-06-04 17:57:00) [19]
> SpellCaster (04.06.07 17:30) [19]
Ничто не обязывает программиста проверять флаг Terminated в теле метода Execute.
SpellCaster (2007-06-04 17:30:00) [20]> [3] clickmaker © (04.06.07 11:42)
Я в курсе )). Но поскольку FTerminated приватная, это единственный способ нормально завершить поток...