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() для правильного запуска