Существует ли стандартный способ вызова с тайм-аутом?
Зависит от того, что вы подразумеваете под «стандартным способом». accept
сам по себе даже не указан стандартом C ++, поэтому, безусловно, нет никакого способа, указанного в C ++. В стандарте POSIX также нет функции accept
, которая бы принимала аргумент времени ожидания, но время ожидания может быть реализовано с использованием стандартных функций POSIX.
Я бы хотел подождать некоторое время
Если вы хотите, чтобы accept
ожидал, т.е. block , вам необходимо установить сокет в режим блокировки.
Чтобы реализовать тайм-аут для accept
(или любого системного вызова блокировки), вы можете использовать таймер POSIX для отправки сигнала, который прервет вызов accept
. После возврата accept
необходимо проверить, успешно ли оно было прервано или прервано по другой причине.
Пример использования таймера POSIX. Принять моделируется с помощью sleep
:
#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
void die(const char*);
volatile sig_atomic_t timeout_reached;
const int timeout_signal = SIGRTMIN;
int main()
{
int limit = 1;
timer_t timer_id;
sigevent sev{};
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = timeout_signal;
if (timer_create(CLOCK_MONOTONIC, &sev, &timer_id))
die("timer_create");
timeout_reached = false;
struct sigaction sa{};
sa.sa_flags = SA_RESETHAND;
sa.sa_handler = [](int) {
timeout_reached = true;
};
if (sigaction(timeout_signal, &sa, nullptr))
die("sigaction");
itimerspec its {};
its.it_value.tv_sec = limit;
if (timer_settime(timer_id, 0, &its, nullptr))
die("timer_settime");
while(!timeout_reached) {
std::cout << "start accepting" << std::endl;
// blocking accept; we simulate it using sleep
sleep(100000);
// check here whether accept succeeded
}
std::cout << "timed out after " << limit << " seconds\n";
}
void die(const char* msg) {
perror(msg);
exit(EXIT_FAILURE);
}
Обратите внимание, что этот пример, вероятно, не будет работать без изменений, если вы используете потоки.
Альтернативный подход заключается в использовании таймера, который использует многопоточный обратный вызов, и в этом обратном вызове подключается к сокету, чтобы остановить блокировку. Полный пример, включающий использование TCP:
#include <iostream>
#include <atomic>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
void die(const char*);
std::atomic<bool> timeout_reached;
constexpr int port = 50000;
int main()
{
int limit = 1;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
die("socket");
sockaddr_in listen_addr{};
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(port);
if(bind(sock, (sockaddr*)&listen_addr, sizeof listen_addr))
die("bind");
if(listen(sock, SOMAXCONN))
die("listen");
timer_t timer_id;
sigevent sev{};
sev.sigev_notify = SIGEV_THREAD;
sev.sigev_notify_function = [](sigval) {
timeout_reached = true;
int client_sock = socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in client_addr;
client_addr.sin_family = AF_INET;
client_addr.sin_port = htons(port);
if(inet_pton(AF_INET, "127.0.0.1", &client_addr.sin_addr) != 1)
die("inet_pton");
if(connect(client_sock, (sockaddr*)&client_addr, sizeof client_addr))
die("connect");
if(close(client_sock))
die("close");
};
if (timer_create(CLOCK_MONOTONIC, &sev, &timer_id))
die("timer_create");
timeout_reached = false;
itimerspec its {};
its.it_value.tv_sec = limit;
if(timer_settime(timer_id, 0, &its, nullptr))
die("timer_settime");
for(;;) {
std::cout << "start accepting" << std::endl;
sockaddr_storage addr;
socklen_t addrlen = sizeof addr;
int fd = accept(sock, (sockaddr*)&addr, &addrlen);
if(fd != -1) {
if (timeout_reached) {
close(fd);
break;
}
// read or whatever
close(fd);
} else {
// handle EAGAIN etc
}
}
std::cout << "timed out after " << limit << " seconds\n";
if(close(sock))
die("close");
}
void die(const char* msg) {
perror(msg);
exit(EXIT_FAILURE);
}
Этот подход может быть проще портировать на определенные системы, которые не поддерживают прерывание accept
.