Ускорение проблемы асинхронного сервера Websocket
Я написал асинхронную веб-сокет через boost.beast. Но когда я пытаюсь запустить его, я не могу подключиться.
Код сервера, как показано ниже. Когда я пытаюсь подключить свой сервер веб-сокетов, мой chrome показывает состояние подключения. Когда я отлаживаю через VS2017, он никогда не сталкивается с лямбда-выражением в run().
iListener::iListener( boost::asio::io_context& ioc,boost::asio::ip::tcp::endpoint endpoint) : acceptor_(ioc), socket_(ioc) {
boost::system::error_code ec;
std::cout<<"iListener"<<std::endl;
// Open the acceptor
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
// fail(ec, "open");
return;
}
// Allow address reuse
acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec);
if (ec) {
// fail(ec, "set_option");
return;
}
// Bind to the server address
acceptor_.bind(endpoint, ec);
if (ec) {
// fail(ec, "bind");
return;
}
// Start listening for connections
acceptor_.listen(
boost::asio::socket_base::max_listen_connections, ec);
if (ec) {
std::cout << ec.message() << " listen" << std::endl;
// fail(ec, "listen");
return;
}
}
iListener::~iListener() {
}
void iListener::run() {
if (!acceptor_.is_open()) {
return;
}
std::cout<<"iListener run"<<std::endl;
while (true) {
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();
}
});
}
}
void iListener::initListener(const std::string &addressStr, unsigned short port, int threads){
auto const address = boost::asio::ip::make_address(addressStr);
boost::asio::io_context ioc{threads};
std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run();
std::vector<std::thread> v;
v.reserve(threads - 1);
for(auto i = threads - 1; i > 0; --i)
v.emplace_back(
[&ioc]
{
ioc.run();
});
ioc.run();
}
И когда я пытаюсь подключить его на консоли Chrome. Это займет много времени для подключения, а затем показывает, что не удалось.
Поэтому я перехожу обратно, как пример boost.It работает.
void iListener::run() {
if (!acceptor_.is_open()) {
return;
}
// std::cout<<"iListener run"<<std::endl;
// while (true) {
// 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();
// }
// });
// }
do_accept();
}
void iListener::do_accept() {
acceptor_.async_accept(
socket_,
std::bind(
&iListener::on_accept,
shared_from_this(),
std::placeholders::_1));
}
void iListener::on_accept(boost::system::error_code ec) {
if (ec)
{
std::cout << ec.message() << " on_accept" << std::endl;
}
else
{
// Create the session and run it
std::make_shared<NormalSession>(std::move(socket_))->run();
}
// Accept another connection
do_accept();
}
У меня есть два вопроса:
1. Почему я использую лямбду, это будет SOF, но пример не будет. 2. Когда я использую while(), это не работает, почему? Есть ли разница между лямбда-выражением и std::bind()??
Итак, еще один вопрос, что отличается между двумя блоками кода ниже?
void iListener::do_accept() {
acceptor_.async_accept(
socket_,
[&](boost::system::error_code ec1) mutable {
on_accept(ec1);
}
}
void iListener::do_accept() {
acceptor_.async_accept(
socket_,
std::bind(
&iListener::on_accept,
shared_from_this(),
std::placeholders::_1));
}
Когда я использую верхний, он возвращает код ошибки 995.
1 ответ
РЕДАКТИРОВАТЬ
В чем разница между bind
а также lambda
? В первом вы продлеваете жизнь iListener
Например, в последнем нет.
Нам нужно начать с этой строки:
std::make_shared<iListener>(ioc, boost::asio::ip::tcp::endpoint{address, port})->run();
// [a]
если вы не продлите жизнь iListener
в run
в строке iListener
экземпляр будет уничтожен.
- станд:: привязывать
Как один из параметров привязки вы проходите shared_from_this
, это создает shared_ptr
от this
указатель, поэтому объект функтора возвращается 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, чтобы увидеть, как async_accept
используется. Обычный способ это позвонить async_accept
от его обработчика, но если вы хотите сделать это, ваш iListener
должно происходить из enable_shared_from_this
продлить свою жизнь при переходе в обработчик.
Еще одна проблема с socket_
элемент данных. Я предполагаю, что вы хотите держать один сокет за сеанс, но теперь ваш код не обрабатывает это правильно. У вас есть только один экземпляр socket_
который перемещен в NormalSession
если новое соединение было установлено. Так когда async_accept
вызывается второй раз, когда вы передаете недействительный сокет. Это не может работать. Это приводит к неопределенному поведению.
После выполнения строки ниже
std::make_shared<NormalSession>(std::move(socket_))->run();
ты можешь забыть о socket_
,
async_accept
перегружен, вы можете использовать обработчик версий с новым принятым сокетом.
Но если вы хотите остаться с текущей версией socket
нужно убедиться, что каждый раз async_accept
называется он принимает уникальный экземпляр сокета.