C ++ Boost.Asio Ошибка сегментации async_write при доступе к методу записи через shared_ptr - PullRequest
0 голосов
/ 22 ноября 2018

Я создал статическую карту, которая содержит несколько сеансов подключенных клиентов.

std::map<std::string, std::shared_ptr<Session>> Communication::m_appSockets;

Слушатель, который принимает входящие клиенты, реализован в классе Communication.

class Communication : public std::enable_shared_from_this<Communication>
{
private:
  boost::asio::io_context m_ioc;
  boost::asio::io_context::work m_work;
  boost::asio::streambuf m_buffer;
  boost::asio::ip::tcp::acceptor m_acceptor;
  std::thread m_senderThread;
  std::thread m_ioThread;

public:
  static std::map<std::string, std::shared_ptr<Session>> m_appSockets;

  Communication(uint16_t t_port);

  void accept();

  void doAccept();

  void senderThread();
};

После принятияклиент метод "doAccept" создает объект сеанса и перемещает сокет следующим образом

  m_acceptor.async_accept(
    [this](boost::system::error_code t_ec, boost::asio::ip::tcp::socket t_socket) {
      if (!t_ec)
      {
            m_appSockets.emplace(std::pair<std::string, std::shared_ptr<Session>>(
              "app0", std::make_shared<Session>(std::move(t_socket))));
            m_appSockets["app0"]->start();
      }
      accept();
    });

Session.h выглядит так:

class Session : public std::enable_shared_from_this<Session>
{
private:
  boost::asio::ip::tcp::socket m_socket;
  boost::asio::streambuf m_buffer;

public:

  Session(boost::asio::ip::tcp::socket t_socket);

  void write(std::string &t_msg);
  void doWrite(std::string &t_msg);
  void start();
...
};

void start () используется для запускаasync читает на сокете, который работает нормальноОбъект сеанса создается следующим образом:

Session::Session(boost::asio::ip::tcp::socket t_socket) : m_socket(std::move(t_socket))
{}

Что мне нужно сделать для моей реализации, так это получить доступ к методу записи сеанса через shared_ptr в карте Communication.h.Я попытался сделать это следующим образом:

void Communication::senderThread()
{
  for (;;)
  {
    .... 
    //blocking until queue holds a message
    std::string buf = *message from queue*//pseudo code
    m_appSockets["app0"].get()->write(buf);

  }
}

Отправитель блокирует поток, пока в очереди не появится сообщение, которое будет перенаправлено в метод записи сеанса

Метод записи можно вызвать, нокак только я пытаюсь выполнить операцию с любым членом сеанса, возникает ошибка сегментации:

void Session::write(std::string &t_msg)
{
//here it crashes
  m_socket.get_executor().context().post(std::bind(&Session::doWrite, shared_from_this(), t_msg));
}

void Session::doWrite(std::string &t_msg)
{
  boost::asio::async_write(
    m_socket, boost::asio::buffer(t_msg),
    std::bind(&Session::onWrite, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
}

Такое ощущение, что объект Session выходит из области видимости, как только я вхожу в его метод.Я попытался создать фиктивные члены в сеансе, которые при доступе к ним давали одинаковую ошибку сегментацииЯ неправильно получаю время жизни shared_ptr / object?

Заранее большое спасибо.

EDIT 1: Запуск ядра gdb ./programm.out дал мне следующее:

Поток 2 "programm.out" получил сигнал SIGSEGV, Ошибка сегментации.[Переключение на поток 0x7ffff58f1700 (LWP 5651)] 0x0000555555605932 в сеансе :: запись (this = 0x0, t_msg = "{\" destination \ ": \" app0 \ "}") в /usr/Sources/Session.cpp:5858 std :: cout << dummyMember << std :: endl; </p>

Я добавил участника в сеанс (int dummyMember {5};).

Как может быть, что это указывает на 0x0?

1 Ответ

0 голосов
/ 22 ноября 2018

Под строчкой подозрительно

  boost::asio::buffer(t_msg)

asio::buffer возвращает объект, который содержит указатель на содержимое строки и длину строки (копия t_msg не создается).Вы должны быть осторожны при использовании asio::buffer с асинхронными операциями, потому что его тип возврата - пара (указатель, длина) для строки, это не продлевает время жизни строки.

async_write функция немедленно возвращается, и мы не знаем, когда будет вызван обработчик, поэтому вы должны убедиться, что msg действует до тех пор, пока не будет вызван обработчик.

  for(;;)
  {
    std::string buf = *message from queue*//pseudo code
    m_appSockets["app0"].get()->write(buf);

    // if you want to send original buf string
    // before this loop ends all asynchronous operation writing buf must be complete
  }

Если ваша цель - отправить msg вы пропустили использование оболочки ref, потому что bind по умолчанию принимает параметры по значению.

std::bind(&Session::doWrite, shared_from_this(), t_msg) // make copy of t_msg

выше создает обратный вызов, который содержит копию t_msg.Когда вызывается этот обратный вызов, копия t_msg передается в boost::asio::buffer(t_msg) в Session::doWrite.Вероятно, перед выполнением обратного вызова, созданного std::bind(&Session::onWrite, shared_from_this(), std::placeholders::_1, std::placeholders::_2), копия строки уничтожается и buffer указывает на удаленные данные.

Вы можете переписать метод Session::write, используя std::ref:

m_socket.get_executor().context().post(std::bind(&Session::doWrite, shared_from_this(), std::ref(t_msg)));

и убедитесь, что все асинхронные операции, которые записывают эту строку, завершены до тех пор, пока цикл for не завершится в senderThread.Или вам нужно найти другой способ хранения строки t_msg во время выполнения асинхронных операций.

...