Как установить тайм-аут сокета в C при создании нескольких соединений?
Я пишу простую программу, которая делает несколько подключений к разным серверам для проверки статуса. Все эти соединения построены по требованию; до 10 соединений можно создать одновременно. Мне не нравится идея одного потока на сокет, поэтому я сделал все эти клиентские сокеты неблокирующими и бросил их в пул select ().
Он работал отлично, пока мой клиент не пожаловался, что время ожидания слишком долго, прежде чем они могут получить отчет об ошибке, когда целевые серверы перестал отвечать.
Я проверил несколько тем на форуме. Некоторые предположили, что можно использовать сигнал тревоги() или установить тайм-аут в вызове функции select (). Но я имею дело с несколькими соединениями, а не с одним. Когда происходит сигнал тайм-аута процесса, я не могу отличить соединение тайм-аута среди всех других соединений.
есть ли в любом случае, чтобы изменить продолжительность таймаута системы по умолчанию?
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 документация onconnect). Вы получите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