РЕДАКТИРОВАТЬ
В чем разница между bind
и lambda
?В первом случае вы продлеваете срок действия экземпляра iListener
, во втором - нет.
Нам нужно начать с этой строки:
std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run();
// [a]
, если вы не продлите время жизни iListener
в run
, в строке [a] экземпляр iListener
будетуничтожены.
В качестве одного из параметров связывания, который вы передаете shared_from_this
, он создает shared_ptr
из this
указателя, поэтому объект functor возвращаетсяна bind
сохраняет умный указатель на iListener
экземпляр продлевает свое время жизни.
время жизни iListener
не продлено, вы вызываете async_accept
мимоходлямбда без увеличения счетчика ссылок для текущего объекта, т.е. для которого вызывается do_accept
.Таким образом, async_accept
возвращается немедленно, do_accept
заканчивается, наконец, run
также заканчивается, и объект, созданный std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})
, удаляется.
Вам необходимо обновить счетчик ссылок, передав shared_ptr
по значению в вашу лямбду:
void iListener::do_accept() {
auto sp = shared_from_this();
acceptor_.async_accept(
socket_,
[&,sp](boost::system::error_code ec1) mutable
{
on_accept(ec1);
}
}
Обработчик (в вашем случае тело лямбды) для задачи, инициированнойasync_accept
вызывается из io_context::run
, вы не можете видеть это выполнение, потому что ваш код висит на этой строке:
std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run();
это создает iListener
экземпляр и вызывает run
, который содержит бесконечный цикл и никогдазаканчивается:
while (true) { // INFINITE LOOP
acceptor_.async_accept(socket_, [&](boost::system::error_code ec1) {
std::cout << "now run listener" << std::endl;
if (ec1) {
std::cout<<ec1.message()<<" accept"<<std::endl;
// fail(ec, "accept");
} else {
// Create the session and run it
std::make_shared<NormalSession>(std::move(socket_))->run();
}
});
}
, поэтому вы не можете добраться до строк, с которых начинается io_context::run
, в которых можно вызывать обработчики.
Исправлено: перед запуском io_context::run
вы можете запустить другой поток, где iListener::run
выполнен.
Посетите Boost Asio examples , чтобы узнать, как используется async_accept
.Обычным способом является вызов async_accept
из его обработчика, но если вы хотите это сделать, ваш iListener
должен получить из enable_shared_from_this
, чтобы продлить срок его службы при переходе в обработчик.
Другая проблемас socket_
членом данных.Я предполагаю, что вы хотите держать один сокет за сеанс, но теперь ваш код не обрабатывает это правильно.У вас есть только один экземпляр socket_
, который перемещается в NormalSession
, если было установлено новое соединение.Поэтому, когда async_accept
вызывается во второй раз, вы передаете сокет INVALID.Это не может работать.Это приводит к неопределенному поведению.
После выполнения строки ниже
std::make_shared<NormalSession>(std::move(socket_))->run();
вы можете забыть о перегрузке socket_
.
async_accept
, вы можете использоватьверсия, принимающая обработчик с недавно принятым сокетом.
Но если вы хотите остаться с текущей версией, принимающей socket
, вам нужно убедиться, что каждый раз, когда вызывается async_accept
, она принимает уникальныеэкземпляр сокета.