[英]Accepting TCP connection with a time-out
是否有标准的方法来调用带有超时的accept
?
我将套接字设置为非阻塞套接字,但它立即返回,并将errno
设置为EAGAIN
,我想等待一段时间,如果成功返回描述符,否则返回-1。 我正在这样做,但是我对此并不满意,我觉得必须有更好的方法。
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;
}
是否有标准的方法来调用带有超时的接受?
取决于您所说的“标准方式”。 accept
本身甚至不是C ++标准指定的,因此C ++当然没有指定任何方法。 在POSIX标准中也没有accept
超时参数的accept
函数,但是可以使用标准POSIX功能来实现超时。
我想等一下
如果要accept
等待,即阻塞 ,则需要将套接字设置为阻塞模式。
要实现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
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.