#scheduling #boost-asio
#планирование #boost-asio
Вопрос:
Я написал программу TCP / IP, в которой клиент использует обычный (не Boost) socket API; т.е. сервер привязывает и прослушивает, а клиент подключается. Сервер использует pthreads для одновременной обработки нескольких клиентов.
Программа использует OpenSSL для обмена сеансовыми ключами между клиентом и сервером.
Позже я заметил, что периодически мне нужно выполнять «повторный ввод ключа»: после установления сеанса я должен запланировать задачу повторного ввода ключа, в которой клиент и сервер согласовывают новый ключ сеанса.
Одним из вариантов было создать новый поток для каждого клиента: Новый поток некоторое время будет находиться в режиме ожидания, а затем проснется и инициирует повторный запуск. Этот вариант довольно дорогой, поскольку каждый клиент должен быть многопоточным.
Другим вариантом было использовать Boost Asio. К сожалению, я не знал о существовании этой ценной библиотеки; в противном случае я бы закодировал программу, используя потоки Boost и сокеты Boost.
В любом случае, я хочу выполнить задание с минимальными изменениями. Сначала я неправильно понял Asio. Используя пример таймера 2 Boost, я написал что-то вроде этого:
void rekey(const boost::system::error_codeamp; /*e*/)
{
// do rekey
}
...
void main()
{
//connect to server
// schedule re-key
boost::asio::io_service io;
boost::asio::deadline_timer t(io, boost::posix_time::minutes(30));
t.async_wait(rekey);
io.run();
// the rest of job (i.e. sending and receiving messages in a while() loop.)
}
Это явно неправильно, поскольку Boost блокируется на io.run()
. Моим первым ощущением было, что асинхронный характер async_wait
решит проблему, но, очевидно, я неправильно понял модель Asio.
У меня есть два решения:
- Сгенерируйте функцию, в которой выполняется отправка и получение (используя обычный socket API), и добавьте эту функцию в очередь
io
объекта, чтобы онаio.run()
работала должным образом. - Перепишите функции сокета, используя API асинхронных сокетов Boost.
Есть ли лучшее решение? В любом случае, не могли бы вы, пожалуйста, помочь мне выяснить, как реализовать наилучшее решение?
Ответ №1:
Как правило, io_service::run()
(или другие функции, вызывающие события, такие как io_service::poll()
, io_servive::run_one()
и io_service::poll_one()
) будут выполняться в их собственном потоке.
Я большой сторонник библиотеки boost :: asio, поскольку, как только вы привыкнете писать с ее помощью, это будет очень элегантная и мощная библиотека. Однако, если у вас уже написана (и работает) большая часть приложения Я бы не советовал переписывать ее только для того, чтобы воспользоваться преимуществами boost:: asio, особенно если вам нужен только какой-то тип асинхронного таймера.
Поскольку вы заявили, что у каждого клиента уже есть свой собственный поток, я бы предложил использовать какой-нибудь тип sentinel, который вы периодически проверяете в этом потоке, чтобы указать, когда был необходим повторный ввод ключа.
Комментарии:
1. Как я могу периодически проверять sentinel? (Например, если я хочу повторно вводить ключ каждые 30 минут.)
2. Если ваш клиентский поток использует неблокирующие операции с сокетами, просто установите простой таймер (GetTickCount() или clock() или time()) в точке входа вашего клиентского потока и периодически проверяйте, не прошло ли текущее время (еще один вызов GetTickCount() или clock() или time()) по сравнению с ожидаемым временем повторного ввода. Однако, если ваш клиент использует блокирующие операции ввода-вывода, это становится сложнее. Вы могли бы добавить дополнительный поток «синхронизации» для управления в этом случае.
Ответ №2:
Перепишите функции сокета, используя API асинхронных сокетов Boost.
Возможно, это не такая сложная задача, как может показаться. Документация включает в себя хорошее сопоставление из BSD socket API с Boost.Asio.