gpt4 book ai didi

c++ - 接受超时的 TCP 连接

转载 作者:太空宇宙 更新时间:2023-11-04 12:44:54 24 4
gpt4 key购买 nike

是否有一种标准的方式来调用带有超时的accept

我将socket设置为非阻塞的,但是它立即返回,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;
}

最佳答案

Is there a standard way of calling accept with a timeout?

取决于您所说的“标准方式”是什么意思。 accept 本身甚至没有被 C++ 标准指定,因此 C++ 肯定没有指定任何方式。在 POSIX 标准中也没有接受超时参数的 accept 函数,但是可以使用标准 POSIX 功能实现超时。

I would like to wait for a period

如果您希望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 的系统。

关于c++ - 接受超时的 TCP 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52210888/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com