Linux: чи є зчитування чи повторне записування з сокета з таймаутом?


105

Як я можу спробувати прочитати дані з сокета з таймаутом? Я знаю, select, pselect, опитування, має поле тайм-ауту, але використання їх вимикає "tcp fast-path" у tcp reno stack.

Єдина моя ідея - використовувати recv (fd, ..., MSG_DONTWAIT) у циклі


Також є можливість використання потоків :), але сигнали потоку все ще потрібні
osgx

Відповіді:


189

Ви можете використовувати функцію setockopt, щоб встановити тайм-аут для операцій прийому:

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. Я перевірив експериментом, що це можна зробити до або після bindLinux та OS X.


1
Ця відповідь врятувала мою дупу. Я застряг у здійсненні цього звивистого лайну "вибору", не маючи успіху. Це спрацювало негайно, набагато простіше.
MiloDC

Тепер саме тому тайм-аут у Windows не працює, оскільки я використовую код для Linux. Дякую. Якщо Windows не використовується, struct timeval tv;то це означає, що select () теж не працює? Я спробував перенести свій select () код до Windows, і він просто таймаутирує миттєво, схоже на його ігнорування значення, яке я встановлюю в timeval.
кучі

1
Я встановлюю значення тайм-ауту на 5 сек. Чому завжди потрібно 5 секунд на кожен цикл читання, незалежно від того, є вхідні дані чи ні?
Хань

це також працює на windows навіть після роботи прив’язки. приміряв на windows 10
cahit beyaz

1
@ user463035818 Ця відповідь стверджує, що не повинна.
Томеаміс

22

Ось простий код для додавання тайм-ауту у вашу recvфункцію за допомогою pollC:

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

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
На це питання вже відповіли роки тому. Яку нову цінність приносить ваше рішення?
Мацей Юречко

Ви встановили цей параметр перед викликом функції bind () для правильного запуску, ця частина не згадується в ans
vivek
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.