我想使用select
函数来处理不同的文件描述符。程序启动时,必须按照间隔开始向其他客户端发送数据包。我的第一个问题是如何在主循环中使用定时器而不中断while循环和select函数的功能?那是因为我需要在计时器运行时接受用户输入。
第二个问题是我现在不需要将send函数放入while循环中。我是否需要在程序进入while循环时编写send函数,或者我需要在其他地方使用它?
此外,程序必须检测在特定时间内未处于活动状态的文件描述符。如何检测哪些文件描述符未向服务器发送任何数据包?
下面你可以看到我到目前为止所写的代码。你能帮我解决一下吗? 我还没有在这里使用任何计时器。此外,程序无法检测哪个文件描述符已超时。
FD_ZERO(&masterfds);
FD_SET(udp_con, &masterfds);
maxfds = udp_con;
while(exit == false)
{ //Do I need to use the send function here?
FD_ZERO(&readfds);
readfds = masterfds;
selectFunc = select(maxfds+1, &readfds, NULL, NULL, &tv);
if(selectFunc < 0)
{
message("error in select");
exit = true;
}
else if(selectFunc == 0)
{ //How can I detect which file descriptor is timed out?
for(i = 0; i <= maxfds; i++)
{
if(FD_ISSET(i, &readfds))
{
//Doesn't work
cout<<"The file descriptor "<<i<<" has timed out"<<endl;
}
}
}
else
{ //The server has received something from a client
for(i = 0; i <= maxfds; i++)
{
}
}
}
答案 0 :(得分:0)
select的最后一个参数是等待文件描述符集中事件的时间量。您可以使用它等待事件的预定义时间段,例如100 ms。如果超时没有收到任何事件,则select将返回0并且不会设置任何描述符。
现在,您需要从服务器的角度考虑“活动”连接的含义。通常,您应该为该连接上最后接收的数据的每个fd保留单独的时间戳。发送可以没有任何问题发生,也不表示连接仍然有效。
所以代码的结构应该是这样的 - 一些粗略的伪代码:
select with timeout
did select return 0:
(this means that no data was received for the timeout on any descriptor)
(this is the place to check if any descriptor went past its 'active' limit)
iterate over all fds:
if last_received_timestamp[current_fd] + deadline > current_time:
remove fd or do something else
else:
(there is something to read)
iterate over descriptors in readfds:
read data and process it
last_received_timestamp[current_fd] = current_time
希望这有帮助。
其他信息:
如果您想要更加统一的方法来处理这个问题,请尝试查看timerfd_create。这可用于创建看起来像文件描述符的计时器,因此您可以使用select来获取某些事件的通知。例如,您可以为每个正常的fd创建一个这样的timerfd,并将其设置为在达到截止日期时到期。这里的关键是每当您收到数据时重新安装计时器。
像这样的单线程循环的经典问题是等待事件然后按顺序处理所有这些循环,如果服务器变得太忙或者连接的数据处理过多,则所有连接都会看到延迟。更好的架构是使用线程池来处理套接字上的读/写。这样可以释放事件循环并为所有客户端提供更好的响应时间。但是,这更复杂,可能超出了您想要实现的范围。