Как установить время ожидания сокета в C при создании нескольких соединений? - PullRequest
61 голосов
/ 15 ноября 2010

Я пишу простую программу, которая делает несколько подключений к разным серверам для проверки состояния.Все эти соединения создаются по требованию;можно создать до 10 соединений одновременно.Мне не нравится идея «один поток на сокет», поэтому я сделал все эти клиентские сокеты неблокирующими и выбросил их в пул select ().жаловался, что время ожидания слишком велико, прежде чем они смогут получить отчет об ошибке, когда целевые серверы перестали отвечать.

Я проверил несколько тем на форуме.Некоторые предположили, что можно использовать сигнал тревоги () или установить тайм-аут при вызове функции select ().Но я имею дело с несколькими связями, а не с одним.Когда происходит сигнал тайм-аута всего процесса, я не могу отличить соединение тайм-аута от всех других соединений.

Есть ли способ изменить длительность тайм-аута по умолчанию для системы?

Ответы [ 5 ]

110 голосов
/ 15 ноября 2010

Вы можете использовать параметры сокета SO_RCVTIMEO и SO_SNDTIMEO, чтобы установить тайм-ауты для любых операций сокета, например:

    struct timeval timeout;      
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
                sizeof(timeout)) < 0)
        error("setsockopt failed\n");

    if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
                sizeof(timeout)) < 0)
        error("setsockopt failed\n");

Редактировать: со страницы руководства setsockopt :

SO_SNDTIMEO - это опция для установки времени ожидания для операций вывода.Он принимает параметр struct timeval с количеством секунд и микросекунд, используемых для ограничения времени ожидания завершения операций вывода.Если операция отправки заблокирована в течение этого времени, она возвращается с частичным счетом или с ошибкой EWOULDBLOCK, если данные не были отправлены.В текущей реализации этот таймер перезапускается каждый раз, когда в протокол доставляются дополнительные данные, подразумевая, что ограничение применяется к выходным частям, размер которых варьируется от отметки низкого уровня до отметки верхнего уровня для вывода.

SO_RCVTIMEO - это опция для установки времени ожидания для операций ввода.Он принимает параметр struct timeval с количеством секунд и микросекунд, используемых для ограничения времени ожидания завершения операций ввода.В текущей реализации этот таймер перезапускается каждый раз, когда протокол получает дополнительные данные, и, таким образом, ограничение является таймером неактивности.Если операция приема была заблокирована в течение этого времени без получения дополнительных данных, она возвращается с коротким счетом или с ошибкой EWOULDBLOCK, если данные не были получены.Параметр struct timeval должен представлять положительный интервал времени;в противном случае setsockopt () возвращается с ошибкой EDOM.

13 голосов
/ 18 октября 2012

я не уверен, полностью ли я понимаю проблему, но думаю, что она связана с той, которая у меня была, я использую Qt с соединением по сокету TCP, все неблокируемые, как в Windows, так и в Linux ..

хотел получить быстрое уведомление о том, что уже подключенный клиент вышел из строя или полностью исчез, и не ждал по умолчанию более 900 секунд, пока не будет подан сигнал отключения. Хитрость для получения этой работы состояла в том, чтобы установить для параметра сокета TCP_USER_TIMEOUT слоя SOL_TCP требуемое значение, заданное в миллисекундах.

это сравнительно новый вариант, пожалуйста, смотрите http://tools.ietf.org/html/rfc5482, но, видимо, он работает нормально, пробовал с WinXP, Win7 / x64 и Kubuntu 12.04 / x64, мой выбор на 10 с оказался немного длиннее , но гораздо лучше, чем все, что я пробовал раньше; -)

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

#ifdef WIN32
    #include <winsock2.h>
#else
    #include <sys/socket.h>
#endif

#ifndef SOL_TCP
    #define SOL_TCP 6  // socket options TCP level
#endif
#ifndef TCP_USER_TIMEOUT
    #define TCP_USER_TIMEOUT 18  // how long for loss retry before timeout [ms]
#endif

установка этой опции сокета работает только тогда, когда клиент уже подключен, строки кода выглядят так:

int timeout = 10000;  // user timeout in milliseconds [ms]
setsockopt (fd, SOL_TCP, TCP_USER_TIMEOUT, (char*) &timeout, sizeof (timeout));

и сбой начального соединения фиксируется таймером, запускаемым при вызове connect (), так как для этого не будет сигнала Qt, сигнал соединения не будет повышен, так как соединения не будет, и Сигнал разъединения также не будет подан, так как соединение еще не было ..

9 голосов
/ 15 ноября 2010

Не можете ли вы реализовать свою собственную систему тайм-аутов?

Сохраните отсортированный список или, что еще лучше, кучу приоритетов событий тайм-аута, как предполагает Хит.В своих вызовах выбора или опроса используйте значение времени ожидания в верхней части списка времени ожидания.Когда наступит этот тайм-аут, выполните это действие с этим тайм-аутом.

Это действие может закрывать сокет, который еще не подключен.

4 голосов
/ 22 января 2013

connect время ожидания должно обрабатываться неблокирующим сокетом (документация GNU LibC на connect). Вы получаете connect для немедленного возврата и затем используете select для ожидания с тайм-аутом для завершения соединения.

Это также объясняется здесь: Ошибка выполнения операции при ошибке подключения (функции) .

int wait_on_sock(int sock, long timeout, int r, int w)
{
    struct timeval tv = {0,0};
    fd_set fdset;
    fd_set *rfds, *wfds;
    int n, so_error;
    unsigned so_len;

    FD_ZERO (&fdset);
    FD_SET  (sock, &fdset);
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    TRACES ("wait in progress tv={%ld,%ld} ...\n",
            tv.tv_sec, tv.tv_usec);

    if (r) rfds = &fdset; else rfds = NULL;
    if (w) wfds = &fdset; else wfds = NULL;

    TEMP_FAILURE_RETRY (n = select (sock+1, rfds, wfds, NULL, &tv));
    switch (n) {
    case 0:
        ERROR ("wait timed out\n");
        return -errno;
    case -1:
        ERROR_SYS ("error during wait\n");
        return -errno;
    default:
        // select tell us that sock is ready, test it
        so_len = sizeof(so_error);
        so_error = 0;
        getsockopt (sock, SOL_SOCKET, SO_ERROR, &so_error, &so_len);
        if (so_error == 0)
            return 0;
        errno = so_error;
        ERROR_SYS ("wait failed\n");
        return -errno;
    }
}
0 голосов
/ 27 мая 2013

Конечно, первый ответ - ЛУЧШИЙ. Можно что-нибудь добавить?

...

После 2 сесцоптов Вы можете контролировать, прошел ли клиент тест тайм-аута или потерпел неудачу с этим:

после

n = readline(sockd, recvline, MAXLINE);

ты должен вставить

if (n <= 0){
    if(write(sockd,"ERROR. Timeout di 5sec scaduto, sii piu' veloce\n",MAXLINE)<0)
        err_sys("errore nella write");
    close(sockd);
    sockd = 0;
    break;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...