Linux: есть ли чтение или recv из сокета с таймаутом?
Как я могу попытаться прочитать данные из сокета с тайм-аутом?
Я знаю, select, pselect, poll, имеет тайм-аут, но использование их отключает "tcp fast-path" в стеке tcp reno.
Единственная идея, которую я имею, это использовать recv (fd,..., MSG_DONTWAIT) в цикле
Ответы
Ответ 1
Вы можете использовать функцию setsockopt, чтобы установить таймаут для операций приема:
SO_RCVTIMEO
Устанавливает значение тайм-аута, которое указывает максимальное время ожидания функции ввода до ее завершения. Он принимает временную структуру с количеством секунд и микросекунд, определяющих ограничение времени ожидания завершения операции ввода. Если операция получения заблокирована в течение этого времени без получения дополнительных данных, она должна вернуться с частичным счетчиком или значением ошибки, равным [EAGAIN] или [EWOULDBLOCK], если данные не получены. Значение по умолчанию для этой опции равно нулю, что указывает на то, что операция приема не должна иметь тайм-аут. Эта опция требует временной структуры. Обратите внимание, что не все реализации позволяют устанавливать эту опцию.
// LINUX
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
// WINDOWS
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
// MAC OS X (identical to Linux)
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
Как сообщается, в Windows это должно быть сделано перед вызовом bind
. Я экспериментально подтвердил, что это можно сделать до или после bind
в Linux и OS X.
Ответ 2
Вот простой код, чтобы добавить тайм-аут к вашей функции recv, используя опрос в C:
struct pollfd fd;
int ret;
fd.fd = mySocket; // your socket handler
fd.events = POLLIN;
ret = poll(&fd, 1, 1000); // 1 second for timeout
switch (ret) {
case -1:
// Error
break;
case 0:
// Timeout
break;
default:
recv(mySocket,buf,sizeof(buf), 0); // get your data
break;
}
Ответ 3
//работает также после операции связывания для WINDOWS
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
Ответ 4
Установите обработчик для SIGALRM
, затем используйте alarm()
или ualarm()
перед регулярной блокировкой recv()
. Если будильник погаснет, recv()
вернет ошибку с errno
, установленным на EINTR
.
Ответ 5
LINUX
struct timeval tv;
tv.tv_sec = 30; // 30 Secs Timeout
tv.tv_usec = 0; // Not init'ing this can cause strange errors
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));
WINDOWS
DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
ПРИМЕЧАНИЕ. Вы установили этот параметр перед вызовом функции bind()
для правильного запуска