На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Соблюдайте общие правила форума
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Указывайте точные версии Delphi и используемых сетевых библиотек.

Не приветствуется поднятие старых тем. Если ваш вопрос перекликается со старой темой, то для вопроса лучше создать новую тему, а старую указать в первом сообщении с описанием взаимосвязи.

Внимание:
попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...

Полезные ссылки:
user posted image MSDN Library user posted image FAQ раздела user posted image Поиск по разделу user posted image Как правильно задавать вопросы


Выразить свое отношение к модераторам раздела можно здесь: user posted image Krid, user posted image Rouse_

Модераторы: Krid, Rouse_
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Timeout подключения TClientSocket
    Здравствуйте!
    TClientSocket в блокирующем режиме при подключении к хосту, который не работает, - довольно долго ждёт, перед тем как выбросить исключение. Можно ли сделать так, чтобы он ждал поменьше?
      Цитата Nagva27 @
      блокирующем режиме при подключении к хосту, который не работает, - довольно долго ждёт

      Это совершенно нормальное состояние блокирующего сокета. У функции connect(), которая осуществляет хендшейк, есть внутренний таймаут, величина которого в разных ОС - разная. Например в BSD это 75 секунд, в некоторых Виндах - 45 секунд и тд.
      Вмешаться в этот процесс в дельфях довольно непросто, но возможно.
      Сообщение отредактировано: Oleg2004 -
        Цитата Oleg2004 @
        Вмешаться в этот процесс в дельфях довольно непросто, но возможно.

        1. Перевести в неблок
        2. Цикл проверки состояния через select + sleep на маленький интервал перед следующей итерацией
        3. При превышении общего времени ожидания - выход по таймауту
        4. Перевести обратно в блок
        5. ???
        6. PROFIT!
          Цитата Fr0sT @
          1. Перевести в неблок
          2. Цикл проверки состояния через select + sleep на маленький интервал перед следующей итерацией
          3. При превышении общего времени ожидания - выход по таймауту
          4. Перевести обратно в блок
          5. ???
          6. PROFIT!

          Вы невластны над внутреним таймаутом функции connect(). :)
          Время посылки повторного сегмента S для RTO рассчитывается по специальной формуле в зависимости от RTT.
          И если даже вы сделаете все как вы пишите в общем то правильно, прекратить исполнение connect() вы не сможете.
          Да, вы сможете вернуться в основную программу по таймауту select(), но connect() все равно будет пытаться соединиться - потому как поставлена в неблокирующее состояние. В винде специально раньше использовалась функция WSACancelBlockingCall() - для этой цели.
          Сообщение отредактировано: Oleg2004 -
            Oleg2004, так и что же можно сделать?
            Сообщение отредактировано: Nagva27 -
              Основная идея исполнения асинхронных функций в Виндовсе типа например WSAAsyncGetHostByName() заключается в том что просто выполняется тот же самый стандартный GetHostByName() - но он выполняется в ОТДЕЛЬНОМ потоке. А вот поток может быть остановлен из главного потока - TerminateThread(). Это основной способ в винде прервать исполнение асинхронной функции. Так что идея в общем то проста - надо исполнять неблокирующий коннект в отдельном предназначенном для этого потоке и через select() (смотрите здесь к примеру) убивать этот поток вместе с функцией если не хотите долго ждать. Я бы лично это не приветствовал - такое поведение коннекта для него родное. Что делать, лучше подождать, когда сам коннект скажет - баста, не хочу.
              Сообщение отредактировано: Oleg2004 -
                Цитата Oleg2004 @
                Вы невластны над внутреним таймаутом функции connect()

                Зато я властен прибить его в любое время
                Цитата Oleg2004 @
                прекратить исполнение connect() вы не сможете

                Closesocket справится.
                Цитата Oleg2004 @
                Так что идея в общем то проста - надо исполнять неблокирующий коннект в отдельном предназначенном для этого потоке и через select() (смотрите здесь к примеру) убивать этот поток вместе с функцией если не хотите долго ждать.

                Какой-то странный набор букв. Зачем поток, если сокет и так неблокирующий? Зачем убивать поток, если есть корректный способ завершения? Зачем ждать, пока коннект соизволит отвалиться сам (таймаут в минуту - суровое испытание нервов юзера)?
                Короче, вот кусок моего класса
                ExpandedWrap disabled
                  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, а вот асинхронку лучше делать на испытанных библиотеках, поскольку режим имеет приличные подводные камни и просто глюки, и при создании собственного велосипеда нарываться на каждый из них будет неприятно.
                Сообщение отредактировано: Fr0sT -
                  Цитата Fr0sT @
                  Closesocket справится.

                  Интересная мысль :)
                  Так лучше программу прибить - крестик справа вверху.

                  Цитата Fr0sT @
                  Какой-то странный набор букв. Зачем поток, если сокет и так неблокирующий? Зачем убивать поток, если есть корректный способ завершения? Зачем ждать, пока коннект соизволит отвалиться сам (таймаут в минуту - суровое испытание нервов юзера)?

                  Какой-то странный набор букв. <_<
                  1. зачем поток. Объясняю, что такое так наз. "неблокирующий" сокет. Во первых, неблокирующих сокетов не существует. Сокеты так сокетами и остаются.
                  Неблокирующими становятся ФУНКЦИИ, которые в противном случае остановили бы исполнение программы на изрядное время.
                  2. Соглашусь, что в вашем коде вы контролируете время установления соединения, при этом ваш код "висит" на селекте. Но исполнение селекта в цикле - это уж слишком. По сути, как я понял (а это всегда нелегко - разбираться в чужом коде :) ) вы просто возвращаете управление в программу по истечении таймаута - но само исполнение коннекта, который теперь работает совершенно автономно - вы прекратить не можете.
                  Кроме как убить сокет.
                  Думаю что в таком случае в Винапи и была придумана функция WSACancelBlockingCall().
                  Тогда коннект исполняется как блокирующий в другом потоке.
                  Вот здесь есть кое-что, и что интересно - ни одного примера с коннектом. Потому как это неизбежно - время на рукопожатие.
                  3. В общем, заморачиваться по этому поводу :"таймаут в минуту - суровое испытание нервов юзера)?" - я не вижу абсолютно никакого смысла. Пусть юзверь понимает, что есть вещи, с которыми мы обязаны сосуществовать. :yes:
                  Сообщение отредактировано: Oleg2004 -
                    Nagva27
                    Можно пропинговать сервер и если нет ответа не выполнять соединение сокетом.
                      ^D^ima
                      Вы неправы. :)
                      Пинг лишь определяет что компутер включен в сеть и на нем функционирует стек TCP/IP
                      Отвечает модуль ICMP, который к никакому серверу не имеет отношения.
                      Сообщение отредактировано: Oleg2004 -
                        Цитата Oleg2004 @
                        Так лучше программу прибить - крестик справа вверху.

                        Цитата Oleg2004 @
                        Объясняю, что такое так наз. "неблокирующий" сокет. Во первых, неблокирующих сокетов не существует. Сокеты так сокетами и остаются.
                        Неблокирующими становятся ФУНКЦИИ, которые в противном случае остановили бы исполнение программы на изрядное время.

                        Передергивать и буквоедствовать — очень продуктивно, да.
                        "неблокирующий" сокет - в просторечии сокет в неблокирующем режиме.
                        Цитата Oleg2004 @
                        Так лучше программу прибить - крестик справа вверху.

                        Придирки не понял. Что ж, раз сокет коннектится - его теперь на трон королевский посадить и пылинки сдувать?
                        Цитата Oleg2004 @
                        2. Соглашусь, что в вашем коде вы контролируете время установления соединения, при этом ваш код "висит" на селекте.

                        "Висит", потому что так сделано. Это простейшая обертка над сокетом, в основном для блокирующего режима, но т.к. создатели библиотеки протупили и не сделали штатного средства регулирования таймаута - приходится делать вот такие кунштюки.
                        Цитата
                        Но исполнение селекта в цикле - это уж слишком.

                        Аргументы?
                        Цитата
                        По сути, как я понял (а это всегда нелегко - разбираться в чужом коде ) вы просто возвращаете управление в программу по истечении таймаута - но само исполнение коннекта, который теперь работает совершенно автономно - вы прекратить не можете.
                        Кроме как убить сокет.

                        А мне плевать на "исполнение коннекта" именно данного конкретного сокета. Что там происходит унутрях сетевого драйвера, какие хэндшейки и ожидания - меня никоим образом не заботит. Не прошел коннект за установленное время - прибиваем хэндл, и пусть система сама дочищает оставшиеся хвосты. В любом случае повлиять на этот процесс никто не может, т.ч. и думать про него нет смысла.
                        Цитата Oleg2004 @
                        Думаю что в таком случае в Винапи и была придумана функция WSACancelBlockingCall().

                        Не помешает почитать справку по этой функции
                        Цитата Oleg2004 @
                        Вот здесь есть кое-что

                        Ага, прекрасная подборка: "not working", "doesn't work", etc :lool:
                        Вообще вся эта система хуков на сокетах какой-то странный неуклюжий, коряво прикрученный костыль. Неудивительно, что его отправили на свалку истории.
                        Цитата Oleg2004 @
                        В общем, заморачиваться по этому поводу :"таймаут в минуту - суровое испытание нервов юзера)?" - я не вижу абсолютно никакого смысла. Пусть юзверь понимает, что есть вещи, с которыми мы обязаны сосуществовать.

                        Отлично! В таком случае желаю всегда сидеть под браузером, подвисающим на полторы минуты для каждого коннекта, ибо сервер недоступен, а есть вещи, "с которыми юзеры обязаны сосуществовать". А еще чтобы ping тоже по такому принципу работал. И мессенджеры.
                          Цитата Oleg2004 @
                          ^D^ima
                          Вы неправы. :)
                          Пинг лишь определяет что компутер включен в сеть и на нем функционирует стек TCP/IP
                          Отвечает модуль ICMP, который к никакому серверу не имеет отношения.

                          Формально вы правы, но это решит проблему "при подключении к хосту, который не работает"
                            Цитата Fr0sT @
                            "неблокирующий" сокет - в просторечии сокет в неблокирующем режиме.

                            Ну вот, уже точнее :)
                            Цитата Fr0sT @
                            А мне плевать на "исполнение коннекта" именно данного конкретного сокета.

                            Если так ставить проблему - то такое решение имеет право на существование.
                            Но оно - такое решение - брутально-кривое. Увы.
                            Цитата Fr0sT @
                            Не помешает почитать справку по этой функции

                            Я с этой функцией работал еще лет 20 тому назад. Так что уверяю вас, что в курсе.
                            Цитата Fr0sT @
                            Вообще вся эта система хуков на сокетах какой-то странный неуклюжий, коряво прикрученный костыль. Неудивительно, что его отправили на свалку истории.

                            Ну, положим система хуков работает до сих пор в каждой винде.
                            А то что функция поддерживается только для обратной совместимости - да такое есть. Но кривые задачи обычно криво и решаются, через другое место.
                            Насчет селекта я писал уже как то давно на нашем форуме. Так что повторяться не буду.
                            А цикл - жрет процессорное время и в результате эффект от автономного исполнения функции чисто в неблокирующем режиме нивелируется. Но для расточительных такое вполне приемлемо.

                            Цитата Fr0sT @
                            В таком случае желаю всегда сидеть под браузером, подвисающим на полторы минуты для каждого коннекта, ибо сервер недоступен,

                            Не проблема. Любой адекватный пользователь знает что поведение сети непредсказуемо, и если одна закладка броузера тупит, делаем другую и работаем с другим сервером, пока не установится соединение или нас не пошлют.
                            Такова селяви однако. Никто не знает, когда установится соединение - "моментально", через 5 сек., через 15 сек или вообще не пройдет. Нетерпеливых просим не беспокоиться. :yes:
                              Цитата Oleg2004 @
                              Если так ставить проблему - то такое решение имеет право на существование.
                              Но оно - такое решение - брутально-кривое. Увы.

                              Чем?
                              Цитата Oleg2004 @
                              Я с этой функцией работал еще лет 20 тому назад.

                              20 лет тому назад и через 20h многое делали, но это же не значит, что так надо делать сейчас.
                              Можно поспорить: провести опрос, кто из здешних вообще знает про такую функцию. Я уж не говорю про применение в проектах.
                              Цитата Oleg2004 @
                              Насчет селекта я писал уже как то давно на нашем форуме. Так что повторяться не буду.

                              Неубедительно
                              Цитата Oleg2004 @
                              А цикл - жрет процессорное время и в результате эффект от автономного исполнения функции чисто в неблокирующем режиме нивелируется. Но для расточительных такое вполне приемлемо.

                              Это одна-то итерация раз в 50 мс жрет? Не смешите мои тапочки.
                              Цитата Oleg2004 @
                              Не проблема. Любой адекватный пользователь знает что поведение сети непредсказуемо, и если одна закладка броузера тупит, делаем другую и работаем с другим сервером, пока не установится соединение или нас не пошлют.
                              Такова селяви однако. Никто не знает, когда установится соединение - "моментально", через 5 сек., через 15 сек или вообще не пройдет. Нетерпеливых просим не беспокоиться.

                              Это вообще без комментариев, по-моему, именно по такому принципу создавалась сетевая часть IE6, который на диалапе мог свести с ума минут за десять.
                                Цитата Fr0sT @
                                20 лет тому назад и через 20h многое делали, но это же не значит, что так надо делать сейчас.
                                Можно поспорить: провести опрос, кто из здешних вообще знает про такую функцию. Я уж не говорю про применение в проектах.

                                Никто - и я тоже - не станет утверждать что такое надо делать сейчас. Есть нормальные асинхронные функции в АПИ сокетов, вот их и надо использовать.
                                Но идея прерывания исполнения коннекта есть нонсенс. Вот и все что я хочу сказать по этому поводу.
                                Весь остальной разговор на эту тему считаю просто троллингом.
                                1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0.0900 ]   [ 15 queries used ]   [ Generated: 23.08.26, 22:46 GMT ]