通过 shared_ptr 访问写入方法时,C++ Boost.Asio async_write 上的分段错误
C++ Boost.Asio Segmentation Fault on async_write while accessing write-method through shared_ptr
我创建了一个静态地图,其中包含多个已连接客户端的会话。
std::map<std::string, std::shared_ptr<Session>> Communication::m_appSockets;
接受传入客户端的侦听器在通信 class 中实现。
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访问session的write-method。
我尝试了以下方法
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对象就超出了作用域。我曾尝试在 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
returns 对象,它包含指向字符串内容和字符串长度的指针(未创建 t_msg
的副本)。在异步操作中使用 asio::buffer
时必须小心,因为它的 return 类型是对 (pointer,length) 到字符串,它不会延长生命周期字符串。
async_write
函数 return 立即生效,我们不知道何时调用处理程序,因此您必须确保 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
的副本将传递到 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
字符串。
我创建了一个静态地图,其中包含多个已连接客户端的会话。
std::map<std::string, std::shared_ptr<Session>> Communication::m_appSockets;
接受传入客户端的侦听器在通信 class 中实现。
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访问session的write-method。 我尝试了以下方法
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对象就超出了作用域。我曾尝试在 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
returns 对象,它包含指向字符串内容和字符串长度的指针(未创建 t_msg
的副本)。在异步操作中使用 asio::buffer
时必须小心,因为它的 return 类型是对 (pointer,length) 到字符串,它不会延长生命周期字符串。
async_write
函数 return 立即生效,我们不知道何时调用处理程序,因此您必须确保 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
的副本将传递到 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
字符串。