通过shared_ptr访问写入方法时,async_write上出现C ++ Boost.Asio分段错误

时间:2018-11-22 09:44:50

标签: c++ segmentation-fault boost-asio shared-ptr

我创建了一个静态地图,其中包含几个连接的客户端的会话。

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

在通信类中实现了接受传入客户端的侦听器。

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()用于启动套接字上的异步读取,可以正常工作。 通过以下方式创建会话对象:

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

对于我的实现,我需要做的是通过Communication.h映射中的shared_ptr访问会话的写方法。 我尝试了以下方式

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

  }
}

senderthread阻塞,直到队列中有可用消息为止,该消息将被转发到会话的write方法

可以调用写方法,但是一旦我在会话的任何成员上尝试操作,就会给我带来分段错误:

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对象超出了范围。我尝试在Session中创建虚拟成员,这些虚拟成员在访问它们时均给出了相同的分段错误。 我在某个时候弄错了shared_ptr / object的生命周期吗?

非常感谢您。

编辑1: 运行gdb ./programm.out核心给了我这个:

  

线程2“ programm.out”接收到信号SIGSEGV,分段错误。   [切换到线程0x7ffff58f1700(LWP 5651)] in 0x0000555555605932   会话::写(此= 0x0,       t_msg =“ {\”目标\“:\” app0 \“}”)       在/usr/Sources/Session.cpp:58 58 std :: cout << dummyMember << std :: endl;

我向Session添加了一个成员(int dummyMember {5};)。

这怎么可能指向0x0?

1 个答案:

答案 0 :(得分:2)

下面一行可疑

  boost::asio::buffer(t_msg)

asio::buffer返回一个对象,该对象持有一个指向字符串内容和字符串长度的指针(未创建t_msg的副本)。将asio::buffer与异步操作一起使用时必须小心,因为它的返回类型是字符串(pointer,length)与字符串对,它不会延长字符串的寿命。

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字符串的方法。