写入套接字后管道损坏
Broken pipe after writing to socket
在我的网络库中,如果我手动 run()
和 restart()
io_context
,我可以对网络进行异步写入。
我现在正尝试通过添加线程池来扩展:
.hpp
struct pool : public std::enable_shared_from_this<pool> {
pool(const pool &) = delete;
auto operator=(const pool &) -> pool & = delete;
explicit pool(pool_parameters config, db_parameters params) noexcept;
asio::io_context m_io_context;
asio::thread_pool m_workers;
asio::executor_work_guard<asio::io_context::executor_type> m_work_guard;
/// \brief Container to hold connections.
std::vector<std::unique_ptr<dbc::connection>> m_connections;
};
.cpp
pool::pool(pool_parameters config, db_parameters params) noexcept
: m_config{std::move(config)},
m_params{std::move(params)},
m_work_guard{asio::make_work_guard(m_io_context)},
m_workers{m_config.thread_pool_size} {
m_connections.reserve(m_config.connection_pool_size);
asio::post(m_workers, [&]() { m_io_context.run(); });
}
管理连接的:
.hpp
struct abstract_connection : connection {
explicit abstract_connection(const std::shared_ptr<pool> &pool) noexcept;
~abstract_connection() override;
packet m_buffer;
asio::local::stream_protocol::endpoint m_endpoint;
asio::generic::stream_protocol::socket m_socket;
asio::io_context::strand m_strand;
};
.cpp
abstract_connection::abstract_connection(const std::shared_ptr<pool> &pool) noexcept
: m_params{pool->m_params},
m_config{pool->m_config},
m_endpoint{pool->m_config.socket},
m_socket{pool->m_io_context},
m_strand{pool->m_io_context} {
m_socket.connect(m_endpoint);
m_socket.non_blocking(true);
}
abstract_connection::~abstract_connection() {
std::error_code ec;
m_socket.shutdown(asio::generic::stream_protocol::socket::shutdown_both, ec);
m_socket.close();
}
现在是混乱的公园。在具体连接对象的构造函数上,我需要进行握手,并在同一 class 的析构函数上进行握手。这并没有发生,因为套接字对象的行为似乎很奇怪:
如果我以异步方式发送数据,则不会向套接字写入任何内容,有时我会收到管道损坏错误:
asio::dispatch(m_strand, [&]() {
m_buffer = write::startup(m_params);
asio::async_write(m_socket, asio::buffer(m_buffer), [](std::error_code ec, std::size_t len) {});
});
如果我进行同步写入,我会在从套接字读取之前收到管道损坏错误:
std::error_code ec;
auto startup = write::startup(m_params);
asio::write(m_socket, asio::buffer(startup), ec);
if (set_error(ec)) {
std::cerr << " XXX " << ec.message() << std::endl;
return;
}
m_buffer.reserve(327);
asio::read(m_socket, asio::buffer(m_buffer), ec);
std::cerr << ec.message() << std::endl;
std::cerr << m_buffer.size() << std::endl;
连接是通过 unix 套接字完成的,我在两者之间安装了 socat,因此我可以看到数据来来去去,以及损坏的管道消息。尝试使用第三方工具连接到远程,所有相关数据都出现在 socat 中,所以我相信问题出在我的代码中。
如何调试套接字的运行情况?
根据您发布的代码,您的 boost::asio::thread_pool
似乎早早超出了范围。你的 abstract_connection
class 只需要一个 const std::shared_ptr<pool> &pool
,这意味着你的抽象连接实例没有在你的线程池上持有引用计数。因此,对 std::shared_ptr
的引用通常没有意义,让您的 abstract_connection
只需在其构造函数中使用 std::shared_ptr<const pool> pool
,您应该将其复制或移动到具有相同类型的成员中。
我通过阻塞套接字 (non_blocking(false)
) 解决了这个问题,如果没有 Superlokkus 的回答,我是不会想到的。
在我的网络库中,如果我手动 run()
和 restart()
io_context
,我可以对网络进行异步写入。
我现在正尝试通过添加线程池来扩展:
.hpp
struct pool : public std::enable_shared_from_this<pool> {
pool(const pool &) = delete;
auto operator=(const pool &) -> pool & = delete;
explicit pool(pool_parameters config, db_parameters params) noexcept;
asio::io_context m_io_context;
asio::thread_pool m_workers;
asio::executor_work_guard<asio::io_context::executor_type> m_work_guard;
/// \brief Container to hold connections.
std::vector<std::unique_ptr<dbc::connection>> m_connections;
};
.cpp
pool::pool(pool_parameters config, db_parameters params) noexcept
: m_config{std::move(config)},
m_params{std::move(params)},
m_work_guard{asio::make_work_guard(m_io_context)},
m_workers{m_config.thread_pool_size} {
m_connections.reserve(m_config.connection_pool_size);
asio::post(m_workers, [&]() { m_io_context.run(); });
}
管理连接的:
.hpp
struct abstract_connection : connection {
explicit abstract_connection(const std::shared_ptr<pool> &pool) noexcept;
~abstract_connection() override;
packet m_buffer;
asio::local::stream_protocol::endpoint m_endpoint;
asio::generic::stream_protocol::socket m_socket;
asio::io_context::strand m_strand;
};
.cpp
abstract_connection::abstract_connection(const std::shared_ptr<pool> &pool) noexcept
: m_params{pool->m_params},
m_config{pool->m_config},
m_endpoint{pool->m_config.socket},
m_socket{pool->m_io_context},
m_strand{pool->m_io_context} {
m_socket.connect(m_endpoint);
m_socket.non_blocking(true);
}
abstract_connection::~abstract_connection() {
std::error_code ec;
m_socket.shutdown(asio::generic::stream_protocol::socket::shutdown_both, ec);
m_socket.close();
}
现在是混乱的公园。在具体连接对象的构造函数上,我需要进行握手,并在同一 class 的析构函数上进行握手。这并没有发生,因为套接字对象的行为似乎很奇怪:
如果我以异步方式发送数据,则不会向套接字写入任何内容,有时我会收到管道损坏错误:
asio::dispatch(m_strand, [&]() {
m_buffer = write::startup(m_params);
asio::async_write(m_socket, asio::buffer(m_buffer), [](std::error_code ec, std::size_t len) {});
});
如果我进行同步写入,我会在从套接字读取之前收到管道损坏错误:
std::error_code ec;
auto startup = write::startup(m_params);
asio::write(m_socket, asio::buffer(startup), ec);
if (set_error(ec)) {
std::cerr << " XXX " << ec.message() << std::endl;
return;
}
m_buffer.reserve(327);
asio::read(m_socket, asio::buffer(m_buffer), ec);
std::cerr << ec.message() << std::endl;
std::cerr << m_buffer.size() << std::endl;
连接是通过 unix 套接字完成的,我在两者之间安装了 socat,因此我可以看到数据来来去去,以及损坏的管道消息。尝试使用第三方工具连接到远程,所有相关数据都出现在 socat 中,所以我相信问题出在我的代码中。
如何调试套接字的运行情况?
根据您发布的代码,您的 boost::asio::thread_pool
似乎早早超出了范围。你的 abstract_connection
class 只需要一个 const std::shared_ptr<pool> &pool
,这意味着你的抽象连接实例没有在你的线程池上持有引用计数。因此,对 std::shared_ptr
的引用通常没有意义,让您的 abstract_connection
只需在其构造函数中使用 std::shared_ptr<const pool> pool
,您应该将其复制或移动到具有相同类型的成员中。
我通过阻塞套接字 (non_blocking(false)
) 解决了这个问题,如果没有 Superlokkus 的回答,我是不会想到的。