Очень кратко резюмировано:
Я реализую простой TCP-сервер с использованием boost :: asio, который позволяет принимать новые соединения без блокировки. Внутри логики обработки нового соединения будет выполняться много работы, которая может занять до нескольких минут. В частности, я собираюсь запустить новый процесс и дождаться его завершения при чтении stdin, stderr и его кода возврата. Просто образ Я хочу запускать g ++ для каждого соединения, компилировать несколько исходных файлов и получать информацию о сборке. Хотя g ++ работает в отдельном процессе, я все еще хочу иметь возможность принимать новые соединения.
Я новичок в поддержке :: 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:?
Есть ли какой-нибудь способ сделать это без необходимости использования вспомогательного потока или процесса для каждого соединения? В конце концов, в какой-то момент мне нужно дождаться результата в моем основном потоке, блокирующем прием новых подключений. Я немного заполучил сопрограмму, но все еще не понимаю ее деталей и не думаю, что она мне поможет в этом сценарии.
Большое спасибо!