Невозможно передать std :: function в обработчик boost :: asio async_accept - PullRequest
0 голосов
/ 17 апреля 2020

Я пытаюсь написать сервер, который принимает пользовательский обработчик от пользователя, используя boost asio. Я могу вызвать операторы из обработчика async_accept, но всякий раз, когда я вызываю обработчик, полученный сервером, это приводит к segfault.

#include <boost/asio.hpp>
#include <functional>
#include <iostream>

using namespace std;
using namespace boost;
using boost::asio::ip::tcp;

class Server {
private:
  asio::io_service &io_service;
  tcp::acceptor acc;
  tcp::endpoint endpoint;

public:
  Server(asio::io_service &io) : io_service{io}, acc{io} {}

  void listen(unsigned short port);
  void accept(function<void()> handler);
};

void Server::listen(unsigned short port) {
  endpoint.port(port);

  acc.open(endpoint.protocol());
  acc.set_option(tcp::acceptor::reuse_address(true));

  acc.bind(endpoint);
  acc.listen();
}

void Server::accept(function<void()> handler) {
  tcp::socket socket(io_service);


  acc.async_accept(socket, [this, h = handler](const system::error_code &error) {
    cout << "Hello World" << endl;

    h();

    accept(h);
  });
}

int main() {
  asio::io_service io_service;
  Server s(io_service);

  s.listen(8000);

  s.accept([]() { cout << "Connection Accepted" << endl; });

  io_service.run();
}

Однако, когда я использую std::bind для лямбды, я передаю его волшебным образом бежит. Я не могу понять, почему и хотел бы также знать, является ли этот подход эффективным?

void Server::accept(function<void()> handler) {
  tcp::socket socket(io_service);

  auto h = std::bind(handler);

  acc.async_accept(socket, [this, h](const system::error_code &error) {
    cout << "Hello World" << endl;

    h();

    accept(h);
  });
}

1 Ответ

1 голос
/ 17 апреля 2020

Проблема не с обработчиком.

Неопределенное поведение вызвано доступом к свисающей ссылке на socket в качестве локальной переменной.

Вы вызываете async_accept, который принимает сокет по ссылке в качестве первого параметра: в соответствии с официальной ссылкой , вызывающая сторона (вы) несет ответственность за продление своего срока службы до вызова обработчика:

Сокет, в который будет принято новое соединение. Право собственности на равноправный объект сохраняется вызывающей стороной, которая должна гарантировать, что он действителен, пока не будет вызван обработчик.

Вы должны заключить сокет в какой-нибудь умный указатель и затем передать этот указатель лямбде чтобы продлить срок его службы:

  std::shared_ptr<tcp::socket> socket = std::make_shared<tcp::socket>(io_service);


  acc.async_accept(*socket, [this, h = handler, socket](const system::error_code &error) {

Надеюсь, вы знаете, что async_XXX немедленно возвращается, следовательно, сокет также удаляется при возврате функции.

...