Как установить тайм-аут сокета в C при создании нескольких соединений?



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



Он работал отлично, пока мой клиент не пожаловался, что время ожидания слишком долго, прежде чем они могут получить отчет об ошибке, когда целевые серверы перестал отвечать.



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



есть ли в любом случае, чтобы изменить продолжительность таймаута системы по умолчанию?

1333   5  

5 ответов:

вы можете использовать параметры сокета 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");

Edit: С setsockoptна странице:

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

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

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

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

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

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

#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, сигнал подключения не будет поднят, так как не будет соединения, и сигнал разъединения также не будет поднят, так как еще не было соединения..

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

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

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

connect тайм-аут должен обрабатываться с помощью неблокирующего сокета (GNU LibC документация on 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;
    }
}

конечно, первый ответ является лучшим. Могу я кое-что добавить?

...

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

после

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;
}

Comments

    Ничего не найдено.