Asio:防止异步客户端被删除

Asio: Prevent asynchronous client from being deleted?

本文关键字:客户端 删除 异步 Asio      更新时间:2023-10-16

我有以下代码,试图编写异步客户端。

问题是在 main() 中,Client在 try-catch 块中删除,因为执行离开了范围。

我试图想出解决这个问题的方法,比如添加一个while(true),但我不喜欢这种方法。另外,我不喜欢getchar().

由于调用的异步性质,connect()loop()都会立即返回。

我该如何解决这个问题?

#include <iostream>
#include <thread>
#include <string>
#include <boostasio.hpp>
#include <Windows.h>
#define DELIM "rn"
using namespace boost;

class Client {
public:
    Client(const std::string& raw_ip_address, unsigned short port_num) :
        m_ep(asio::ip::address::from_string(raw_ip_address), port_num), m_sock(m_ios)
    {
        m_work.reset(new asio::io_service::work(m_ios));
        m_thread.reset(new std::thread([this]() {
            m_ios.run();
        }));
        m_sock.open(m_ep.protocol());
    }
    void connect()
    {
        m_sock.async_connect(m_ep, [this](const system::error_code& ec)
        {
            if (ec != 0) {
                std::cout << "async_connect() error: " << ec.message() << " (" << ec.value() << ") " << std::endl;
                return;
            }
            std::cout << "Connection to server has been established." << std::endl;
        });
    }
    void loop() 
    {
        std::thread t = std::thread([this]() 
        {
            recv();
        });
        t.join();
    }
    void recv()
    {
        asio::async_read_until(m_sock, buf, DELIM, [this](const system::error_code& ec, std::size_t bytes_transferred)
        {
            if (ec != 0) {
                std::cout << "async_read_until() error: " << ec.message() << " (" << ec.value() << ") " << std::endl;
                return;
            }
            std::istream is(&buf);
            std::string req;
            std::getline(is, req, 'r');
            is.get(); // discard newline
            std::cout << "Received: " << req << std::endl;
            if (req == "alive") {
                recv();
            }
            else if (req == "close") {
                close();
                return;
            }
            else {
                send(req + DELIM);
            }
        });
    }
    void send(std::string resp)
    {
        auto s = std::make_shared<std::string>(resp);
        asio::async_write(m_sock, asio::buffer(*s), [this, s](const system::error_code& ec, std::size_t bytes_transferred)
        {
            if (ec != 0) {
                std::cout << "async_write() error: " << ec.message() << " (" << ec.value() << ") " << std::endl;
                return;
            }
            else {
                recv();
            }
        });
    }
    void close()
    {
        m_sock.close();
        m_work.reset();
        m_thread->join();
    }
private:
    asio::io_service m_ios;
    asio::ip::tcp::endpoint m_ep;
    asio::ip::tcp::socket m_sock;
    std::unique_ptr<asio::io_service::work> m_work;
    std::unique_ptr<std::thread> m_thread;
    asio::streambuf buf;
};
int main()
{
    const std::string raw_ip_address = "127.0.0.1";
    const unsigned short port_num = 8001;
    try {
        Client client(raw_ip_address, port_num);
        client.connect();
        client.loop();
    }
    catch (system::system_error &err) {
        std::cout << "main() error: " << err.what() << " (" << err.code() << ") " << std::endl;
        return err.code().value();
    }
    return 0;
}

你还没有真正理解asio是如何工作的。通常在主线程中,您将调用io_service::run()(它将处理所有异步事件)。

要确保Client的生存期,请使用shared_ptr<>并确保在处理程序中使用此共享指针。例如。。

io_service service;
{
  // Create the client - outside of this scope, asio will manage
  // the life time of the client
  auto client = make_shared<Client>(service);
  client->connect(); // setup the connect operation..
}
// Now run the io service event loop - this will block until there are no more
// events to handle
service.run();

现在,您需要重构Client代码:

class Client : public std::enable_shared_from_this<Client> {
Client(io_service& service): socket_(service) ... 
{ }
void connect() {
  // By copying the shared ptr to the lambda, the life time of
  // Client is guaranteed
  socket_.async_connect(endpoint_, [self = this->shared_from_this()](auto ec)
    {
      if (ec) {
        return;
      }
      // Read
      self->read(self);
    });
}
void read(shared_ptr<Client> self) {
  // By copying the shared ptr to the lambda, the life time of
  // Client is guaranteed
  asio::async_read_until(socket_, buffer_, DELIM, [self](auto ec, auto size)
  {
    if (ec) {
      return;
    }
    // Handle the data
    // Setup the next read operation
    self->read(self)
  });
}
};

您有一个用于读取操作的线程 - 这不是必需的。这将注册一个异步读取操作并立即返回。您需要注册一个新的读取操作才能继续读取套接字(正如我所勾勒的那样......

您可以通过 post(Handler) 将任何函数发布到io_servicehttp://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/io_service/post.html

然后在 main() 中执行以下操作:

while (!exit) {
  io_service.run_one();
}

或者调用 io_service::run_one 或 io_service::在 main() 中运行