Як я можу спробувати прочитати дані з сокета з таймаутом? Я знаю, select, pselect, опитування, має поле тайм-ауту, але використання їх вимикає "tcp fast-path" у tcp reno stack.
Єдина моя ідея - використовувати recv (fd, ..., MSG_DONTWAIT) у циклі
Як я можу спробувати прочитати дані з сокета з таймаутом? Я знаю, select, pselect, опитування, має поле тайм-ауту, але використання їх вимикає "tcp fast-path" у tcp reno stack.
Єдина моя ідея - використовувати recv (fd, ..., MSG_DONTWAIT) у циклі
Відповіді:
Ви можете використовувати функцію 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
. Я перевірив експериментом, що це можна зробити до або після bind
Linux та OS X.
struct timeval tv;
то це означає, що select () теж не працює? Я спробував перенести свій select () код до Windows, і він просто таймаутирує миттєво, схоже на його ігнорування значення, яке я встановлюю в timeval.
Ось простий код для додавання тайм-ауту у вашу 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)
байтів, змушуючи її знову блокуватися, якщо цей підрахунок ще не прийшов, але на цей раз, не маючи тайм-ауту.
// працює також після операції прив’язки для WINDOWS
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
Встановіть обробник для SIGALRM
, потім використовуйте alarm()
або ualarm()
перед звичайним блокуванням recv()
. Якщо будильник вимкнеться, recv()
повідомлення поверне помилку із errno
встановленим значенням на EINTR
.
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()
викликом функції для правильного запуску