转移boost::asio::socket堆栈变量的所有权
Transfer ownership of boost::asio::socket stack variable
我正在编写一个能够处理多个并发连接的简单tcp套接字服务器。其想法是,主侦听线程将执行阻塞接受并将套接字句柄卸载到工作线程(线程池中(,以从那里异步处理通信。
void server::run() {
{
io_service::work work(io_service);
for (std::size_t i = 0; i < pool_size; i++)
thread_pool.push_back(std::thread([&] { io_service.run(); }));
boost::asio::io_service listener;
boost::asio::ip::tcp::acceptor acceptor(listener, ip::tcp::endpoint(ip::tcp::v4(), port));
while (listening) {
boost::asio::ip::tcp::socket socket(listener);
acceptor.accept(socket);
io_service.post([&] {callback(std::move(socket));});
}
}
for (ThreadPool::iterator it = thread_pool.begin(); it != thread_pool.end(); it++)
it->join();
}
我在堆栈上创建socket
,因为我不想在while(listening)
循环中重复分配内存。
回调函数callback
具有以下原型:
void callback(boost::asio::socket socket);
我的理解是,调用callback(std::move(socket))
将把socket
的所有权转移到callback
。然而,当我试图从callback
内部调用socket.receive()
时,我得到了一个Bad file descriptor
错误,所以我认为这里有问题。
如何将socket
的所有权转移到回调函数,理想情况下不必在堆上创建套接字?
未定义的行为可能会被调用,因为lambda可能会通过悬挂引用在先前销毁的套接字上调用std::move()
。例如,考虑包含套接字的循环在调用lambda之前结束其当前迭代,导致socket
被破坏的情况:
Main Thread | Thread Pool
-----------------------------------+----------------------------------
tcp::socket socket(...); |
acceptor.accept(socket); |
io_service.post([&socket] {...}); |
~socket(); // end iteration |
... // next iteration | callback(std::move(socket));
要解决此问题,需要将socket
所有权转移到处理程序,而不是在处理程序内转移所有权。根据文档,Handlers必须是CopyConstructible
,因此它们的参数,包括不可复制的socket
,也必须是。然而,如果Asio可以消除对处理程序的复制构造函数的所有调用,并且已经定义了BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
,那么这个要求就可以放宽。
#define BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
#include <boost/asio.hpp>
void callback(boost::asio::ip::tcp::socket socket);
...
// Transfer ownership of socket to the handler.
io_service.post(
[socket=std::move(socket)]() mutable
{
// Transfer ownership of socket to `callback`.
callback(std::move(socket));
});
有关Asio类型检查的更多详细信息,请参阅此答案。
下面是一个完整的例子,演示了socket
的所有权被转移到一个处理程序:
#include <functional> // std::bind
#include <utility> // std::move
#include <vector> // std::vector
#define BOOST_ASIO_DISABLE_HANDLER_TYPE_REQUIREMENTS
#include <boost/asio.hpp>
const auto noop = std::bind([]{});
void callback(boost::asio::ip::tcp::socket socket)
{
const std::string actual_message = "hello";
boost::asio::write(socket, boost::asio::buffer(actual_message));
}
int main()
{
using boost::asio::ip::tcp;
// Create all I/O objects.
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
tcp::socket client_socket(io_service);
// Connect the sockets.
client_socket.async_connect(acceptor.local_endpoint(), noop);
{
tcp::socket socket(io_service);
acceptor.accept(socket);
// Transfer ownership of socket to the handler.
assert(socket.is_open());
io_service.post(
[socket=std::move(socket)]() mutable
{
// Transfer ownership of socket to `callback`.
callback(std::move(socket));
});
assert(!socket.is_open());
} // ~socket
io_service.run();
// At this point, sockets have been conencted, and `callback`
// should have written data to `client_socket`.
std::vector<char> buffer(client_socket.available());
boost::asio::read(client_socket, boost::asio::buffer(buffer));
// Verify the correct message was read.
const std::string expected_message = "hello";
assert(std::equal(
begin(buffer), end(buffer),
begin(expected_message), end(expected_message)));
}
相关文章:
- 堆栈中大小变量输入错误 (C++)
- 变量周围的堆栈'...'已损坏
- 在 gtest 中初始化堆栈上的引用变量的隔离错误
- 运行时检查失败 #2 变量"A"周围的堆栈已损坏
- 如果我们通过引用传递变量,则递归中使用的堆栈空间量是否为零?
- 在 c++ 中确定堆栈上的变量范围
- 为什么在堆栈和堆上创建变量会产生相同的程序集代码?
- 为什么弹出我的堆栈会返回垃圾而不是初始变量?
- 堆栈粉碎 在我在代码中添加新变量以及一些操作后C++检测到
- 无法在具有常量变量大小的类中创建堆栈分配数组
- 运行时检查失败 #2 - 变量"e"周围的堆栈已损坏。发生
- 堆栈/帧指针作为外部变量
- "new"创建的实例的所有成员变量是否都存在于堆上而不是堆栈上?
- 变量周围的堆栈'sortArray'已损坏
- 堆栈变量超出范围时是否解除分配?
- 变量周围的堆栈'folderPath'已损坏
- 何时在函数中声明堆栈分配变量?
- 运行时检查失败 #2 - 变量周围的堆栈'...'已损坏
- C++ 大于堆栈的变量(堆栈溢出)
- 局部变量堆栈