[英]Accepting TCP connection with a time-out
Is there a standard way of calling accept
with a timeout? 是否有标准的方法来调用带有超时的
accept
?
I am setting the socket as a non-blocking, but it immediately returns with errno
set to EAGAIN
, I would like to wait for a period, if succeed return descriptor, if not, return -1. 我将套接字设置为非阻塞套接字,但它立即返回,并将
errno
设置为EAGAIN
,我想等待一段时间,如果成功返回描述符,否则返回-1。 I am doing this, but I don;t feel good about it and I feel like there has to be a better way. 我正在这样做,但是我对此并不满意,我觉得必须有更好的方法。
template <class Rep, class Period>
socket_handler_t wait_for_connection(const std::chrono::duration<Rep, Period> &timeout_duration)
{
set_nonblocking();
auto c_lambda = [](int fd) -> int {
struct sockaddr_storage conn_addr_;
int addrlen = sizeof(conn_addr_);
return accept(fd, (struct sockaddr *)&conn_addr_, (socklen_t *)&addrlen);
};
auto wait_ms = std::chrono::duration_cast<std::chrono::milliseconds>(timeout_duration);
wait_ms /= 10;
socket_handler_t connfd = -1;
auto count = 0U;
while (count < 10)
{
count++;
connfd = c_lambda(socket_handle);
if (errno == EWOULDBLOCK || errno == EAGAIN)
{
std::puts(std::to_string(wait_ms.count()).c_str());
std::this_thread::sleep_for(timeout_duration);
continue;
}
else
{
break;
}
}
set_blocking();
return connfd;
}
Is there a standard way of calling accept with a timeout?
是否有标准的方法来调用带有超时的接受?
Depends on what you mean by "standard way". 取决于您所说的“标准方式”。
accept
itself isn't even specified by the C++ standard, so there is certainly no way specified by C++. accept
本身甚至不是C ++标准指定的,因此C ++当然没有指定任何方法。 There is no accept
function that would take a timeout argument in POSIX standard either, but a timeout can be implemented using standard POSIX functionality. 在POSIX标准中也没有
accept
超时参数的accept
函数,但是可以使用标准POSIX功能来实现超时。
I would like to wait for a period
我想等一下
If you want accept
to wait ie block , you need to set the socket to blocking mode. 如果要
accept
等待,即阻塞 ,则需要将套接字设置为阻塞模式。
To implement a timeout for accept
(or any blocking system call), you can use a POSIX timer to send a signal that will interrupt the accept
call. 要实现
accept
(或任何阻塞的系统调用)超时,可以使用POSIX计时器发送一个信号,该信号将中断accept
调用。 After accept
returns, you need to check whether it succeeded, or was interrupted, or failed for other reason. accept
返回后,您需要检查它是否成功,被中断或由于其他原因失败。
Example using a POSIX timer. 使用POSIX计时器的示例。 Accept is simulated using
sleep
: 接受是使用
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);
}
Note that this example probably won't work without modification if you use threads. 请注意,如果您使用线程,则此示例在不进行修改的情况下可能无法工作。
An alternative approach, is to use a timer that uses a threaded callback, and within that callback connect to the socket to stop the blocking. 一种替代方法是使用计时器,该计时器使用线程回调,并在该回调内连接到套接字以停止阻塞。 A full example including the use of TCP:
包括使用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);
}
This approach may be easier to port to certain systems that don't support interruption of accept
. 这种方法可能更容易移植到某些不支持中断
accept
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.