我创建了一个静态映射,其中包含几个连接的客户端的 session 。
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”创建一个 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核心给了我这个:
我向 session 添加了一个成员(int dummyMember {5};)。
这怎么可能指向0x0?
最佳答案
下线可疑
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
字符串的方法。