正确的超时非阻塞套接字事件处理?
Proper timeout nonblock socket event handling?
我没看到有人问过这类问题。这很奇怪,因为单线程服务器应用程序带来了很多好处。但是,当服务器处于非阻塞状态时,我如何能够在我的代码中实现超时系统?
目前我正在使用这个方法
while(true)
{
FD_ZERO(&readfds);
FD_SET(server_socket, &readfds);
for (std::size_t i = 0; i < cur_sockets.size(); i++)
{
uint32_t sd = cur_sockets.at(i).socket;
if(sd > 0)
FD_SET(sd, &readfds);
if(sd > max_sd){
max_sd = sd;
}
}
int activity = select( max_sd + 1 , &readfds, NULL , NULL, NULL);
if(activity < 0)
{
continue;
}
if (FD_ISSET(server_socket, &readfds))
{
struct sockaddr_in cli_addr;
uint32_t newsockfd = (uint_fast32_t)accept((int)server_socket,
(struct sockaddr *) &cli_addr,
&clientlength);
if(newsockfd < 1) {
continue;
}
//Ensure we can even accept the client...
if (num_clients >= op_max_clients) {
close(newsockfd);
continue;
}
fcntl(newsockfd, F_SETFL, O_NONBLOCK);
/* DISABLE TIMEOUT EXCEPTION FROM SIGPIPE */
#ifdef __APPLE__
int set = 1;
setsockopt(newsockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &set, sizeof(int));
#elif __LINUX__
signal(SIGPIPE, SIG_IGN);
#endif
/* ONCE WE ACCEPTED THE CONNECTION ADD CLIENT TO */
num_clients++;
client_con newCon;
newCon.socket = newsockfd;
time_t ltime;
time(<ime);
newCon.last_message = (uint64_t) ltime;
cur_sockets.push_back(newCon);
}
handle_clients();
}
如您所知,我已经在客户端成功连接时向其添加了一个 unix 时间戳。我正在考虑添加另一个每 1 秒休眠一次的线程,并简单地检查是否有任何客户端在最大持续时间内没有进行任何发送,但我担心我会 运行 因为第二个而陷入瓶颈处理大量连接时线程不断锁定。
谢谢,
阿吉姆.
select 的最后一个参数是 select 调用的超时时间,select 的 return 代码告诉您,如果它 returned因为套接字已就绪或超时。
为了对所有套接字实施您自己的超时处理,您可以为每个套接字设置一个时间戳,并在任何套接字操作时更新它。然后在调用 select 之前计算每个套接字的超时,并使用 select 调用的超时的最小值。这只是基本思想,可以更有效地实现它,这样您就不需要在调用 select 之前重新计算所有超时。但我认为一个单独的线程是多余的。
我没看到有人问过这类问题。这很奇怪,因为单线程服务器应用程序带来了很多好处。但是,当服务器处于非阻塞状态时,我如何能够在我的代码中实现超时系统?
目前我正在使用这个方法
while(true)
{
FD_ZERO(&readfds);
FD_SET(server_socket, &readfds);
for (std::size_t i = 0; i < cur_sockets.size(); i++)
{
uint32_t sd = cur_sockets.at(i).socket;
if(sd > 0)
FD_SET(sd, &readfds);
if(sd > max_sd){
max_sd = sd;
}
}
int activity = select( max_sd + 1 , &readfds, NULL , NULL, NULL);
if(activity < 0)
{
continue;
}
if (FD_ISSET(server_socket, &readfds))
{
struct sockaddr_in cli_addr;
uint32_t newsockfd = (uint_fast32_t)accept((int)server_socket,
(struct sockaddr *) &cli_addr,
&clientlength);
if(newsockfd < 1) {
continue;
}
//Ensure we can even accept the client...
if (num_clients >= op_max_clients) {
close(newsockfd);
continue;
}
fcntl(newsockfd, F_SETFL, O_NONBLOCK);
/* DISABLE TIMEOUT EXCEPTION FROM SIGPIPE */
#ifdef __APPLE__
int set = 1;
setsockopt(newsockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &set, sizeof(int));
#elif __LINUX__
signal(SIGPIPE, SIG_IGN);
#endif
/* ONCE WE ACCEPTED THE CONNECTION ADD CLIENT TO */
num_clients++;
client_con newCon;
newCon.socket = newsockfd;
time_t ltime;
time(<ime);
newCon.last_message = (uint64_t) ltime;
cur_sockets.push_back(newCon);
}
handle_clients();
}
如您所知,我已经在客户端成功连接时向其添加了一个 unix 时间戳。我正在考虑添加另一个每 1 秒休眠一次的线程,并简单地检查是否有任何客户端在最大持续时间内没有进行任何发送,但我担心我会 运行 因为第二个而陷入瓶颈处理大量连接时线程不断锁定。
谢谢,
阿吉姆.
select 的最后一个参数是 select 调用的超时时间,select 的 return 代码告诉您,如果它 returned因为套接字已就绪或超时。
为了对所有套接字实施您自己的超时处理,您可以为每个套接字设置一个时间戳,并在任何套接字操作时更新它。然后在调用 select 之前计算每个套接字的超时,并使用 select 调用的超时的最小值。这只是基本思想,可以更有效地实现它,这样您就不需要在调用 select 之前重新计算所有超时。但我认为一个单独的线程是多余的。