简体   繁体   English

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

[英]Asio: Prevent asynchronous client from being deleted?

I have the following code, trying to code an asynchronous client. 我有以下代码,尝试编写异步客户端。

The problem is that in main() , the Client gets deleted in the try-catch block, because execution leaves the scope. 问题在于在main()Client会在try-catch块中删除,因为执行会离开作用域。

I've tried to come up with a solution to this problem, like adding a while(true) , but I don't like this approach. 我试图为这个问题提供解决方案,例如添加while(true) ,但是我不喜欢这种方法。 Also, I don't prefer a getchar() . 另外,我也不喜欢getchar()

Due to the asynchronous nature of the calls, both connect() and loop() returns immediately. 由于调用的异步特性, connect()loop()立即返回。

How can I fix this? 我怎样才能解决这个问题?

#include <iostream>
#include <thread>
#include <string>

#include <boost\asio.hpp>
#include <Windows.h>

#define DELIM "\r\n"

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;
}

You've not really understood how asio works. 您还不太了解asio的工作原理。 Typically in the main thread(s) you will call io_service::run() (which will handle all the asynchronous events.) 通常,在主线程中,您将调用io_service::run() (它将处理所有异步事件。)

To ensure the lifetime of the Client , use a shared_ptr<> and ensure this shared pointer is used in the handlers. 为了确保Client的生命周期,请使用shared_ptr<>并确保在处理程序中使用此共享指针。 For example.. 例如..

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();

Now you need to refactor your Client code: 现在,您需要重构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)
  });
}
};

You have a thread for the read operation - which is not necessary. 您具有用于读取操作的线程-这不是必需的。 That will register one async read operation and return immediately. 这将注册一个异步读取操作并立即返回。 You need to register a new read operation to continue reading the socket (as I've sketched out..) 您需要注册一个新的读取操作才能继续读取套接字(如我所概述的。)

You can post any function to io_service via post(Handler) http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/io_service/post.html 您可以通过post(Handler)将任何函数发布到io_service上http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/io_service/post.html

Then in the main() do something like: 然后在main()中执行以下操作:

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

Or call io_service::run_one or io_service::run in the main() 或在main()中调用io_service :: run_one或io_service :: run

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM