Timeout подключения TClientSocket
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.223] |
|
|
Соблюдайте общие правила форума
MSDN Library
FAQ раздела
Поиск по разделу
Как правильно задавать вопросы| Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Timeout подключения TClientSocket
|
Сообщ.
#1
,
|
|
|
|
Здравствуйте!
TClientSocket в блокирующем режиме при подключении к хосту, который не работает, - довольно долго ждёт, перед тем как выбросить исключение. Можно ли сделать так, чтобы он ждал поменьше? |
|
Сообщ.
#2
,
|
|
|
|
Цитата Nagva27 @ блокирующем режиме при подключении к хосту, который не работает, - довольно долго ждёт Это совершенно нормальное состояние блокирующего сокета. У функции connect(), которая осуществляет хендшейк, есть внутренний таймаут, величина которого в разных ОС - разная. Например в BSD это 75 секунд, в некоторых Виндах - 45 секунд и тд. Вмешаться в этот процесс в дельфях довольно непросто, но возможно. |
|
Сообщ.
#3
,
|
|
|
|
Цитата Oleg2004 @ Вмешаться в этот процесс в дельфях довольно непросто, но возможно. 1. Перевести в неблок 2. Цикл проверки состояния через select + sleep на маленький интервал перед следующей итерацией 3. При превышении общего времени ожидания - выход по таймауту 4. Перевести обратно в блок 5. ??? 6. PROFIT! |
|
Сообщ.
#4
,
|
|
|
|
Цитата Fr0sT @ 1. Перевести в неблок 2. Цикл проверки состояния через select + sleep на маленький интервал перед следующей итерацией 3. При превышении общего времени ожидания - выход по таймауту 4. Перевести обратно в блок 5. ??? 6. PROFIT! Вы невластны над внутреним таймаутом функции connect(). Время посылки повторного сегмента S для RTO рассчитывается по специальной формуле в зависимости от RTT. И если даже вы сделаете все как вы пишите в общем то правильно, прекратить исполнение connect() вы не сможете. Да, вы сможете вернуться в основную программу по таймауту select(), но connect() все равно будет пытаться соединиться - потому как поставлена в неблокирующее состояние. В винде специально раньше использовалась функция WSACancelBlockingCall() - для этой цели. |
|
Сообщ.
#5
,
|
|
|
|
Oleg2004, так и что же можно сделать?
|
|
Сообщ.
#6
,
|
|
|
|
Основная идея исполнения асинхронных функций в Виндовсе типа например WSAAsyncGetHostByName() заключается в том что просто выполняется тот же самый стандартный GetHostByName() - но он выполняется в ОТДЕЛЬНОМ потоке. А вот поток может быть остановлен из главного потока - TerminateThread(). Это основной способ в винде прервать исполнение асинхронной функции. Так что идея в общем то проста - надо исполнять неблокирующий коннект в отдельном предназначенном для этого потоке и через select() (смотрите здесь к примеру) убивать этот поток вместе с функцией если не хотите долго ждать. Я бы лично это не приветствовал - такое поведение коннекта для него родное. Что делать, лучше подождать, когда сам коннект скажет - баста, не хочу.
|
|
Сообщ.
#7
,
|
|
|
|
Цитата Oleg2004 @ Вы невластны над внутреним таймаутом функции connect() Зато я властен прибить его в любое время Цитата Oleg2004 @ прекратить исполнение connect() вы не сможете Closesocket справится. Цитата Oleg2004 @ Так что идея в общем то проста - надо исполнять неблокирующий коннект в отдельном предназначенном для этого потоке и через select() (смотрите здесь к примеру) убивать этот поток вместе с функцией если не хотите долго ждать. Какой-то странный набор букв. Зачем поток, если сокет и так неблокирующий? Зачем убивать поток, если есть корректный способ завершения? Зачем ждать, пока коннект соизволит отвалиться сам (таймаут в минуту - суровое испытание нервов юзера)? Короче, вот кусок моего класса ![]() ![]() const WaitIntrv = 50; // [msec] wait for socket to be ready SleepIntrv = 100; // [msec] wait between state check attempts ... try ... // Establish connection using non-blocking mode (to be able to respect specified Timeout ) elapse := FTimeout; if SleepIntrv < FTimeout then currsleep := SleepIntrv else currsleep := FTimeout; OldBlocking := Blocking; Blocking := False; if WinSock.connect(FSckt, pAddr^, SizeOf(TSockAddr)) = SOCKET_ERROR then begin // connect error? CheckWSResult(WSAGetLastError = WSAEWOULDBLOCK, 'connect'); // else - connect in process - repeat checking socket state repeat if elapse <= 0 then // time is out begin WSASetLastError(WSAETIMEDOUT); CheckWSResult(False, 'connect'); end; FD_ZERO(Wfd); FD_SET(FSckt, Wfd); FD_ZERO(EFd); FD_SET(FSckt, Efd); TimeVal.tv_sec := 0; TimeVal.tv_usec := WaitIntrv; // check socket state res := select(0, nil, @Wfd, @Efd, @TimeVal); CheckWSResult(res <> SOCKET_ERROR, 'select'); case res of // not ready - wait 0: begin Sleep(currsleep); Dec(elapse, currsleep); end; // ready - check result 1: if FD_ISSET(FSckt, Efd) then // error - get error code and exit begin Len := SizeOf(err); if getsockopt(FSckt, SOL_SOCKET, SO_ERROR, @err, Len) = SOCKET_ERROR then err := WSAECONNREFUSED; WSASetLastError(err); CheckWSResult(False, 'connect'); end else if FD_ISSET(FSckt, Wfd) then Break // OK - finish the loop else; // smth else else CheckWSResult(False, 'select'); end; // case until False; end; Blocking := OldBlocking; FActive := True; ... except // error - close socket err := WSAGetLastError; Close; WSASetLastError(err); raise; end; Добавлено Очевидно, на время ожидания таймаута основной поток "зависнет", что можно решить, размазав логику (напр., выполнять проверки по таймеру, естественно, уже без Sleep), либо вынеся сокет в отдельный тред, либо совсем кардинально - перейдя на асинхронную модель. Для числа одновременных коннектов <= 10 асинхронка - оверкилл, легче и проще задействовать треды. К тому же синхронку можно довольно легко реализовать самому на Winapi, а вот асинхронку лучше делать на испытанных библиотеках, поскольку режим имеет приличные подводные камни и просто глюки, и при создании собственного велосипеда нарываться на каждый из них будет неприятно. |
|
Сообщ.
#8
,
|
|
|
|
Цитата Fr0sT @ Closesocket справится. Интересная мысль Так лучше программу прибить - крестик справа вверху. Цитата Fr0sT @ Какой-то странный набор букв. Зачем поток, если сокет и так неблокирующий? Зачем убивать поток, если есть корректный способ завершения? Зачем ждать, пока коннект соизволит отвалиться сам (таймаут в минуту - суровое испытание нервов юзера)? Какой-то странный набор букв. 1. зачем поток. Объясняю, что такое так наз. "неблокирующий" сокет. Во первых, неблокирующих сокетов не существует. Сокеты так сокетами и остаются. Неблокирующими становятся ФУНКЦИИ, которые в противном случае остановили бы исполнение программы на изрядное время. 2. Соглашусь, что в вашем коде вы контролируете время установления соединения, при этом ваш код "висит" на селекте. Но исполнение селекта в цикле - это уж слишком. По сути, как я понял (а это всегда нелегко - разбираться в чужом коде ) вы просто возвращаете управление в программу по истечении таймаута - но само исполнение коннекта, который теперь работает совершенно автономно - вы прекратить не можете. Кроме как убить сокет. Думаю что в таком случае в Винапи и была придумана функция WSACancelBlockingCall(). Тогда коннект исполняется как блокирующий в другом потоке. Вот здесь есть кое-что, и что интересно - ни одного примера с коннектом. Потому как это неизбежно - время на рукопожатие. 3. В общем, заморачиваться по этому поводу :"таймаут в минуту - суровое испытание нервов юзера)?" - я не вижу абсолютно никакого смысла. Пусть юзверь понимает, что есть вещи, с которыми мы обязаны сосуществовать. |
|
Сообщ.
#9
,
|
|
|
|
Nagva27
Можно пропинговать сервер и если нет ответа не выполнять соединение сокетом. |
|
Сообщ.
#10
,
|
|
|
|
^D^ima
Вы неправы. Пинг лишь определяет что компутер включен в сеть и на нем функционирует стек TCP/IP Отвечает модуль ICMP, который к никакому серверу не имеет отношения. |
|
Сообщ.
#11
,
|
|
|
|
Цитата Oleg2004 @ Так лучше программу прибить - крестик справа вверху. Цитата Oleg2004 @ Объясняю, что такое так наз. "неблокирующий" сокет. Во первых, неблокирующих сокетов не существует. Сокеты так сокетами и остаются. Неблокирующими становятся ФУНКЦИИ, которые в противном случае остановили бы исполнение программы на изрядное время. Передергивать и буквоедствовать — очень продуктивно, да. "неблокирующий" сокет - в просторечии сокет в неблокирующем режиме. Цитата Oleg2004 @ Так лучше программу прибить - крестик справа вверху. Придирки не понял. Что ж, раз сокет коннектится - его теперь на трон королевский посадить и пылинки сдувать? Цитата Oleg2004 @ 2. Соглашусь, что в вашем коде вы контролируете время установления соединения, при этом ваш код "висит" на селекте. "Висит", потому что так сделано. Это простейшая обертка над сокетом, в основном для блокирующего режима, но т.к. создатели библиотеки протупили и не сделали штатного средства регулирования таймаута - приходится делать вот такие кунштюки. Цитата Но исполнение селекта в цикле - это уж слишком. Аргументы? Цитата По сути, как я понял (а это всегда нелегко - разбираться в чужом коде ) вы просто возвращаете управление в программу по истечении таймаута - но само исполнение коннекта, который теперь работает совершенно автономно - вы прекратить не можете. Кроме как убить сокет. А мне плевать на "исполнение коннекта" именно данного конкретного сокета. Что там происходит унутрях сетевого драйвера, какие хэндшейки и ожидания - меня никоим образом не заботит. Не прошел коннект за установленное время - прибиваем хэндл, и пусть система сама дочищает оставшиеся хвосты. В любом случае повлиять на этот процесс никто не может, т.ч. и думать про него нет смысла. Цитата Oleg2004 @ Думаю что в таком случае в Винапи и была придумана функция WSACancelBlockingCall(). Не помешает почитать справку по этой функции Цитата Oleg2004 @ Вот здесь есть кое-что Ага, прекрасная подборка: "not working", "doesn't work", etc Вообще вся эта система хуков на сокетах какой-то странный неуклюжий, коряво прикрученный костыль. Неудивительно, что его отправили на свалку истории. Цитата Oleg2004 @ В общем, заморачиваться по этому поводу :"таймаут в минуту - суровое испытание нервов юзера)?" - я не вижу абсолютно никакого смысла. Пусть юзверь понимает, что есть вещи, с которыми мы обязаны сосуществовать. Отлично! В таком случае желаю всегда сидеть под браузером, подвисающим на полторы минуты для каждого коннекта, ибо сервер недоступен, а есть вещи, "с которыми юзеры обязаны сосуществовать". А еще чтобы ping тоже по такому принципу работал. И мессенджеры. |
|
Сообщ.
#12
,
|
|
|
|
Цитата Oleg2004 @ ^D^ima Вы неправы. Пинг лишь определяет что компутер включен в сеть и на нем функционирует стек TCP/IP Отвечает модуль ICMP, который к никакому серверу не имеет отношения. Формально вы правы, но это решит проблему "при подключении к хосту, который не работает" |
|
Сообщ.
#13
,
|
|
|
|
Цитата Fr0sT @ "неблокирующий" сокет - в просторечии сокет в неблокирующем режиме. Ну вот, уже точнее Цитата Fr0sT @ А мне плевать на "исполнение коннекта" именно данного конкретного сокета. Если так ставить проблему - то такое решение имеет право на существование. Но оно - такое решение - брутально-кривое. Увы. Цитата Fr0sT @ Не помешает почитать справку по этой функции Я с этой функцией работал еще лет 20 тому назад. Так что уверяю вас, что в курсе. Цитата Fr0sT @ Вообще вся эта система хуков на сокетах какой-то странный неуклюжий, коряво прикрученный костыль. Неудивительно, что его отправили на свалку истории. Ну, положим система хуков работает до сих пор в каждой винде. А то что функция поддерживается только для обратной совместимости - да такое есть. Но кривые задачи обычно криво и решаются, через другое место. Насчет селекта я писал уже как то давно на нашем форуме. Так что повторяться не буду. А цикл - жрет процессорное время и в результате эффект от автономного исполнения функции чисто в неблокирующем режиме нивелируется. Но для расточительных такое вполне приемлемо. Цитата Fr0sT @ В таком случае желаю всегда сидеть под браузером, подвисающим на полторы минуты для каждого коннекта, ибо сервер недоступен, Не проблема. Любой адекватный пользователь знает что поведение сети непредсказуемо, и если одна закладка броузера тупит, делаем другую и работаем с другим сервером, пока не установится соединение или нас не пошлют. Такова селяви однако. Никто не знает, когда установится соединение - "моментально", через 5 сек., через 15 сек или вообще не пройдет. Нетерпеливых просим не беспокоиться. |
|
Сообщ.
#14
,
|
|
|
|
Цитата Oleg2004 @ Если так ставить проблему - то такое решение имеет право на существование. Но оно - такое решение - брутально-кривое. Увы. Чем? Цитата Oleg2004 @ Я с этой функцией работал еще лет 20 тому назад. 20 лет тому назад и через 20h многое делали, но это же не значит, что так надо делать сейчас. Можно поспорить: провести опрос, кто из здешних вообще знает про такую функцию. Я уж не говорю про применение в проектах. Цитата Oleg2004 @ Насчет селекта я писал уже как то давно на нашем форуме. Так что повторяться не буду. Неубедительно Цитата Oleg2004 @ А цикл - жрет процессорное время и в результате эффект от автономного исполнения функции чисто в неблокирующем режиме нивелируется. Но для расточительных такое вполне приемлемо. Это одна-то итерация раз в 50 мс жрет? Не смешите мои тапочки. Цитата Oleg2004 @ Не проблема. Любой адекватный пользователь знает что поведение сети непредсказуемо, и если одна закладка броузера тупит, делаем другую и работаем с другим сервером, пока не установится соединение или нас не пошлют. Такова селяви однако. Никто не знает, когда установится соединение - "моментально", через 5 сек., через 15 сек или вообще не пройдет. Нетерпеливых просим не беспокоиться. Это вообще без комментариев, по-моему, именно по такому принципу создавалась сетевая часть IE6, который на диалапе мог свести с ума минут за десять. |
|
Сообщ.
#15
,
|
|
|
|
Цитата Fr0sT @ 20 лет тому назад и через 20h многое делали, но это же не значит, что так надо делать сейчас. Можно поспорить: провести опрос, кто из здешних вообще знает про такую функцию. Я уж не говорю про применение в проектах. Никто - и я тоже - не станет утверждать что такое надо делать сейчас. Есть нормальные асинхронные функции в АПИ сокетов, вот их и надо использовать. Но идея прерывания исполнения коннекта есть нонсенс. Вот и все что я хочу сказать по этому поводу. Весь остальной разговор на эту тему считаю просто троллингом. |