Отмена повышения :: asio :: async_read изящно - PullRequest
0 голосов
/ 29 марта 2020

У меня есть класс, который выглядит следующим образом:

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 (e c), он не вызывает handleReadProtocol с этим e c. Сокет отключается, но обработчик не вызывается.

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

И shared_ptr объекта MyConnector, который был передан с помощью shared_from_this (), не освобождается. Объект остается как зомб ie в куче памяти. Как мне отменить async_read () таким образом, чтобы счетчик ссылок на объекты MyConnector уменьшался, позволяя объекту уничтожать себя?

1 Ответ

0 голосов
/ 29 марта 2020

Две вещи: во-первых, в 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()));
     }
  });
...