Я хочу использовать select
функция для работы с различными файловыми дескрипторами. Когда программа запускается, она должна начать отправку пакетов другим клиентам в соответствии с интервалом. Мой первый вопрос: как я могу использовать таймер в основном цикле while, не прерывая цикл while и функциональность функции select? Это потому, что мне нужно принимать пользовательские вводы, когда таймер работает.
Второй вопрос заключается в том, что я не знаю, нужно ли мне поместить функцию send в мой цикл while. Нужно ли писать функцию отправки, когда программа входит в цикл while, или мне нужно использовать ее где-нибудь еще?
Также программа должна обнаружить файловые дескрипторы, которые не были активны в течение определенного времени. Как я могу определить, какие файловые дескрипторы не отправляли какие-либо пакеты на сервер?
Ниже вы можете увидеть коды, которые я написал до сих пор. Не могли бы вы помочь мне исправить это?
Я еще не использовал таймер здесь. Кроме того, программа не может определить, для какого дескриптора файла истекло время ожидания.
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++)
{
}
}
}
Последний аргумент select — это время ожидания событий в наборе дескрипторов файла. Вы можете использовать это, чтобы ждать в течение предопределенного периода времени для событий, скажем, 100 мс. Если этот тайм-аут прошел без получения каких-либо событий, select вернет 0, и дескрипторы не будут установлены.
Теперь вам нужно подумать о том, что означает «активное» соединение с точки зрения сервера. Обычно вы должны хранить отдельную временную метку для каждого поля, когда данные были последними получено на этой связи. Отправка может происходить без проблем и не означает, что соединение все еще активно.
Таким образом, код должен быть структурирован так — какой-то грубый псевдокод:
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 для них, чтобы получать уведомления об определенных событиях. Например, вы можете создать один такой timerfd для каждого имеющегося у вас обычного fd и установить его срок действия по истечении крайнего срока. Ключевым моментом здесь является повторное включение таймера при получении данных.
Классическая проблема с однопоточными циклами, такими как этот, которые ожидают события, затем обрабатывают их все последовательно, состоит в том, что если сервер становится слишком занятым или если обработка данных для соединения занимает слишком много времени, все соединения будут видеть задержки. Лучшей архитектурой является использование пула потоков для обработки чтения / записи в сокеты. Это освобождает цикл обработки событий и обеспечивает лучшее время отклика для всех клиентов. Однако это более сложно и, вероятно, выходит за рамки того, что вы пытаетесь достичь.
Других решений пока нет …