Linux: есть ли чтение или получение из сокета с таймаутом?

106

Как я могу попытаться прочитать данные из сокета с таймаутом? Я знаю, что select, pselect, poll имеет поле тайм-аута, но их использование отключает "tcp fast-path" в стеке tcp reno.

Единственная идея - использовать recv (fd, ..., MSG_DONTWAIT) в цикле

osgx
источник
Также есть возможность использовать потоки :), но сигналы потоков все еще необходимы
osgx 04

Ответы:

189

Вы можете использовать функцию 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.

Роберт С. Барнс
источник
1
Этот ответ спас мою задницу. Я застрял в реализации этой запутанной ерунды «отбор», но безуспешно. Это сработало сразу, намного проще.
MiloDC
Вот почему тайм-аут в окнах не работает, потому что я использую код для Linux. Спасибо. Если окна не используются, struct timeval tv;значит ли это, что select () тоже не работает? Я попытался перенести свой код select () в окна, и он сразу же просто отключился, похоже, что он игнорирует значение, которое я устанавливаю в timeval.
kuchi
1
Я установил значение тайм-аута на 5 секунд. Почему каждый цикл чтения всегда занимает 5 секунд, вне зависимости от того, есть ли входящие данные или нет?
Хан
это также работает с окнами даже после операции привязки. попробовал на windows 10
cahit beyaz
1
@ user463035818 Этот ответ утверждает, что не должен.
Tomeamis
22

Вот простой код для добавления тайм-аута к вашей recvфункции, используя pollв 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;
}
Абдессамад Даугри
источник
это не будет работать так, как ожидалось. pollбудет ждать получения хотя бы одного байта или тайм-аута, тогда как при вызове recvфункции он будет ждать sizeof(buf)байтов, заставляя снова блокироваться, если этот счетчик еще не прибыл, но на этот раз без тайм-аута.
LoPiTaL
0

// работает также после операции привязки для WINDOWS

DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
Cahit Beyaz
источник
-1

Установите обработчик для SIGALRM, затем используйте alarm()или ualarm()перед штатной блокировкой recv(). Если будильник recv()сработает , будет возвращена ошибка со errnoзначением EINTR.

кафе
источник
8
сигнализация (и сигналы) - неправильный путь к этой задаче. Если я хочу использовать быстрый путь tcp, мне нужна минимальная задержка. Сигналы медленные.
osgx 04
2
@osgx Сигнал появляется только в случае тайм-аута.
Дэвид Шварц
-4

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

ОКНА

DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));

ПРИМЕЧАНИЕ . Вы установили этот параметр перед bind()вызовом функции для правильного запуска.

вивек
источник
4
На этот вопрос уже дан ответ много лет назад. Какую новую ценность приносит ваше решение?
Maciej Jureczko
Вы установили этот параметр перед вызовом функции bind () для правильного выполнения, эта часть не упоминается в ans
vivek