通过 shared_ptr 访问写方法时 async_write 上的 C++ Boost.Asio 段错误

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

我创建了一个静态 map ,其中包含多个已连接客户端的 session 。

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

接受传入客户端的 Listener 在 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”方法创建一个 session 对象并像这样移动套接字

  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::Session(boost::asio::ip::tcp::socket t_socket) : m_socket(std::move(t_socket))
{}

我需要为我的实现做的是通过 Communication.h 映射中的 shared_ptr 访问 session 的写入方法。 我尝试了以下方法

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 阻塞直到队列中的消息可用,该消息将被转发到 session 的 write 方法

可以调用写入方法,但是一旦我尝试对 session 的任何成员进行操作,它就会给我一个段错误:

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 core 给了我这个:

Thread 2 "programm.out" received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7ffff58f1700 (LWP 5651)] 0x0000555555605932 in Session::write (this=0x0, t_msg="{\"destination\":\"app0\"}") at /usr/Sources/Session.cpp:58 58 std::cout << dummyMember << std::endl;

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

这怎么可能指向0x0呢?

最佳答案

下面一行是可疑的

  boost::asio::buffer(t_msg)

asio::buffer 返回包含指向字符串内容和字符串长度的指针的对象(未创建 t_msg 的拷贝)。在异步操作中使用 asio::buffer 时必须小心,因为它的返回类型是字符串对 (pointer,length) ,它不会延长字符串。

async_write 函数会立即返回,我们不知道 handler 何时会被调用,因此您必须确保 msg 有效,直到 handler 被调用。

  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 的拷贝被传递到 Session::doWrite 中的 boost::asio::buffer(t_msg)。可能,在 std::bind(&Session::onWrite, shared_from_this(), std::placeholders::_1, std::placeholders::_2) 创建的回调被执行之前,字符串拷贝被销毁并且buffer 指向删除的数据。

您可以使用 std::ref 重写 Session::write 方法:

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

并确保所有写入此字符串的异步操作都已完成,直到 for 循环在 senderThread 结束。或者您需要找到另一种方法来在执行异步操作时保存 t_msg 字符串。

关于通过 shared_ptr 访问写方法时 async_write 上的 C++ Boost.Asio 段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53427997/

相关文章:

c++ - 在网上找类似的源码

c - 分割字符串的函数有时会出现段错误

c++ - 如何捕获 boost asio boost::system::error_code 连接异常与其他异常分开?

c++ - 使用 boost::serialization 保存数据时出现段错误

java - BufferedReader.read 在读取整个消息之前返回 -1

c++ - 阻塞 Boost Asio 工作线程

c++ - 重载运算符 [] 以接受范围

c++ - Borland 中的 bcc32 和 bcc32ide 有什么区别?

c++ - 当 64 位 int 在 C/C++ 中转换为 64 位 float 并且没有完全匹配时,它是否总是落在非小数上?

c - 如何调试这个段错误?