优雅地取消 boost::asio::async_read

Posted

技术标签:

【中文标题】优雅地取消 boost::asio::async_read【英文标题】:Cancelling boost::asio::async_read gracefully 【发布时间】:2020-03-29 15:33:43 【问题描述】:

我有一个如下所示的课程:

class MyConnector : public boost::noncopyable, public boost::enable_shared_from_this<MyConnector>

public:
    typedef MyConnector this_type;
    boost::asio::ip::tcp::socket _plainSocket;
    boost::shared_ptr<std::vector<uint8_t>> _readBuffer;

    // lot of obvious stuff removed....

    void readProtocol()
    
        _readBuffer = boost::make_shared<std::vector<uint8_t>>(12, 0);
        boost::asio::async_read(_plainSocket, boost::asio::buffer(&_readBuffer->at(0), 12),
                boost::bind(&this_type::handleReadProtocol, shared_from_this(), 
                boost::asio::placeholders::bytes_transferred, boost::asio::placeholders::error));
    

    void handleReadProtocol(size_t bytesRead,const boost::system::error_code& error)
    
        // handling code removed
    
;

此类实例通常在尝试读取完整消息之前等待接收 12 字节协议。但是,当我尝试取消此读取操作并销毁对象时,它不会发生。当我调用 _plainSocket.cancel(ec) 时,它不会使用该 ec 调用 handleReadProtocol。套接字断开连接,但未调用处理程序。

boost::system::error_code ec;
_plainSocket.cancel(ec);

并且未释放使用 shared_from_this() 传递的 MyConnector 对象的 shared_ptr。该对象在堆内存中仍然像僵尸一样。如何以递减 MyConnector 对象引用计数的方式取消 async_read(),从而允许对象自行销毁?

【问题讨论】:

请提供minimal reproducible example 【参考方案1】:

两件事:第一,在handleReadProtocol 中,确保如果出现错误,不会调用readProtocol。取消的操作仍然调用处理程序,但设置了错误代码。

其次,asio 建议在连接完成后关闭并关闭套接字。例如:

  asio::post([this] 
     if (_plainSocket.is_open()) 
        asio::error_code ec;
        /* For portable behaviour with respect to graceful closure of a connected socket, call
         * shutdown() before closing the socket. */
        _plainSocket.shutdown(asio::ip::tcp::socket::shutdown_both, ec);
        if (ec) 
           Log(fmt::format("Socket shutdown error .", ec.message()));
           ec.clear();
        
        _plainSocket.close(ec);
        if (ec)
           Log(fmt::format("Socket close error .", ec.message()));
     
  );

【讨论】:

谢谢。起初,即使这样也行不通。不久之后,我注意到在调用 close() 之后有 ioservice.stop()。这没有给套接字足够的时间关闭。现在我在延迟后打电话。

以上是关于优雅地取消 boost::asio::async_read的主要内容,如果未能解决你的问题,请参考以下文章

使用 boost::asio::async_wait_until 和 boost::asio::streambuf

关于创建 boost.asio async_xxxx 处理程序对象

当 streambuf 由先前的 async_read 填充时, boost::asio::async_read 进入 boost::asio::streambuf 块

Boost::asio::async_read 不会在条件下停止

Boost Asio async_send 产生哔声

传递给 boost::asio::async_read_some 的回调从未在 boost::asio::read_some 返回数据的使用中调用