Планирование задачи в Boost при продолжении текущей работы

#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.

У меня есть два решения:

  1. Сгенерируйте функцию, в которой выполняется отправка и получение (используя обычный socket API), и добавьте эту функцию в очередь io объекта, чтобы она io.run() работала должным образом.
  2. Перепишите функции сокета, используя 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.