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


106

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

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


Также есть возможность использовать потоки :), но сигналы потоков все еще необходимы
— 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);

-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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.