【问题标题】:Broken pipe after writing to socket写入套接字后管道损坏
【发布时间】:2019-02-27 15:03:10
【问题描述】:

在我的网络库中,如果我手动 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();
    }

现在是令人困惑的公园。在具体连接对象的 ctor 上,我需要进行握手,同时对同一类的析构函数进行握手。这不会发生,因为套接字对象的行为似乎很奇怪:

如果我异步发送数据,则不会向套接字写入任何内容,有时会出现管道损坏错误:

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 中,所以我相信问题出在我的代码中。

如何调试套接字的情况?

【问题讨论】:

  • 很难从这样的部分代码中判断出了什么问题。但是,您发布的代码中存在一个可疑的 -&gt;shared_from_this() 缺失,因此问题很可能是生命周期管理不善。将日志代码添加到 ctor/dtor 通常有助于跟踪这些类型的错误。

标签: c++ c++11 boost-asio


【解决方案1】:

根据您发布的代码,您的boost::asio::thread_pool 似乎很早就超出了范围。你的abstract_connection 类只需要一个const std::shared_ptr&lt;pool&gt; &amp;pool,这意味着你的抽象连接实例在你的线程池中没有引用计数。因此,对std::shared_ptr 的引用通常没有意义,让您的abstract_connection 在其构造函数中使用std::shared_ptr&lt;const pool&gt; pool,您应该将其复制或移动到具有相同类型的成员中。

【讨论】:

  • 但是同步调用应该阻塞线程,直到所有 I/O 都完成不是吗?
  • 不,它不会阻塞,因为 thread_pool::~thread_pool “如果没有事先明确完成,自动停止并加入池。”,所以你的 async_write 将被中止。
【解决方案2】:

我通过阻止套接字 (non_blocking(false)) 解决了这个问题,如果没有 Superlokkus 的回答,我不会想到。

【讨论】:

  • 我认为你不得不这样做是因为生命周期被破坏了,作为一个现实生活中的例子,我可以参考我的项目github.com/Superlokkus/RTSP/blob/master/src/streaming_lib/…,但总的来说我推荐youtube.com/watch?v=rwOv_tw2eA4,即github.com/CppCon/CppCon2016/blob/master/Tutorials/…跨度>
  • @Superlokkus non_blocking 只影响阻塞调用。当我使用异步调用时,我在测试中添加了一个 sleep(),并在使用同步调用时将套接字设置为阻塞。
  • 是的,但是您最初的问题是您的阻塞调用会在调用时直接返回 boost::asio::error::would_block 错误,而问题是您没有对此进行检查。
  • 这是真的,我应该检查一下。
  • 睡眠在我的测试中,不在生产代码中。
猜你喜欢
  • 2011-11-04
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 2011-07-13
  • 2012-08-25
  • 2010-12-25
相关资源
最近更新 更多