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

时间:2016-03-22 11:02:56

标签: c++ asynchronous boost boost-asio

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

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

我试图找到解决此问题的方法,例如添加while(true),但我不喜欢这种方法。另外,我不喜欢getchar()

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

我该如何解决这个问题?

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

2 个答案:

答案 0 :(得分:1)

你真的不明白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)
  });
}
};

您有一个读取操作的线程 - 这是不必要的。这将注册一个异步读取操作并立即返回。您需要注册一个新的读取操作以继续读取套接字(因为我已经勾画出来了......)

答案 1 :(得分:0)

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

然后在main()做类似的事情:

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

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