Очень кратко подытожено:
Я внедряю простой TCP-сервер, использующий boost :: asio, который позволяет неблокировать принятие новых соединений.Внутри логики обработки нового соединения будет выполнена большая работа, которая может занять до нескольких минут.В частности, я собираюсь запустить новый процесс и дождаться его завершения при чтении stdin, stderr и его кода возврата.Просто я хочу запустить g ++ для каждого соединения, скомпилировать несколько исходных файлов и получить информацию о сборке.Пока g ++ работает в отдельном процессе, я все еще хочу принимать новые соединения.
Я новичок в boost :: asio и ищу советы по дизайну и вклад в мои текущие идеи.
Вариант № 1: Использование потока для каждого соединения и отсоединение его
#include <boost/asio.hpp>
#include "tcp_connection.hpp"
#include <thread>
#include <functional>
using boost::asio::ip::tcp;
class tcp_server
{
public:
tcp_server(boost::asio::io_context& io_context, unsigned short port_num)
: m_io_context(io_context),
m_acceptor(io_context, tcp::endpoint(tcp::v4(), port_num)),
m_port_num(port_num)
{
// create initial connection that will be accepted
create_connection();
}
private:
void create_connection()
{
// create new connection that will be accepted
tcp_connection::pointer new_connection = tcp_connection::create(m_io_context);
// can't mix std::bind with boost::asio::placeholders ...
m_acceptor.async_accept(new_connection->socket(),
boost::bind(&tcp_server::handle_accept, this,
boost::asio::placeholders::error));
// save new connection to be handled next
m_curr_connection = new_connection;
}
void handle_accept(const boost::system::error_code& error)
{
if(!error)
{
// run new connection in own thread
std::thread t(std::bind(&tcp_connection::run, m_curr_connection));
// create next connection that will be accepted
create_connection();
// detach thread before it goes out of scope
t.detach();
}
}
boost::asio::io_context& m_io_context;
tcp::acceptor m_acceptor;
tcp_connection::pointer m_curr_connection;
unsigned short m_port_num;
};
Таким образом, все соединения принимаются асинхронно в основном потоке с использованием async_accept.Для обработки я создаю рабочий поток, который вызывает g ++ и ожидает его завершения.Тем не менее, сервер может принимать новые соединения и запускать новые компиляции.
Метод запуска соединения выглядит примерно так, без обработки ошибок
auto prog = boost::process::search_path("g++");
boost::asio::io_context io_context;
std::future<std::string> data;
boost::process::child processCobol(prog, "main.cpp"
boost::process::std_in.close(),
boost::process::std_out > boost::process::null,
boost::process::std_err > data,
io_context);
io_context.run();
m_message = data.get();
Я использую асинхронный ввод-выводздесь также, однако, было бы также достаточно прочитать результаты синхронно для меня на данный момент.
Вариант № 2: Использование подхода вилки
ПредположимУ меня есть libg++
, который я могу связать прямо с сервером.Я мог бы использовать «классический» подход к вилке для каждого соединения, как показано здесь: https://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/example/fork/process_per_connection.cpp и поставить вызов на g++_compile()
сразу после вилки.Тем не менее я могу принять новое соединение, так как у меня есть отдельный процесс для каждого соединения.
Опция № 3: Используйте boost :: process :: spawn и считывать результат через разделяемую память, например, используя boost ::interprocess
Вместо создания дочернего процесса я мог бы запустить новый процесс, отсоединить его и в какой-то момент прочитать результат обратно через общую память.Что-то подсказывает мне, что это не очень хорошая идея. D:
Вариант № 4:?
Есть ли способ сделать это без необходимости использованиявспомогательный поток или процесс для каждого соединения?В конце концов, в какой-то момент мне нужно дождаться результата в моем основном потоке, блокирующем прием новых соединений.Я немного заполучил сопрограмму, но все еще не понимаю ее деталей и не думаю, что она мне тоже поможет в этом сценарии.
Большое спасибо!