【问题标题】:libboost ASIO. Simple asynchronous client serverlibboost ASIO。简单的异步客户端服务器
【发布时间】:2011-08-04 13:46:03
【问题描述】:

我正在尝试在 ASIO 中实现一个简单的客户端/服务器。

我想要服务器端的以下内容:

onConnect()
onDisconnect()
onMessageRecieved(char* data)
sendMessage(char* data)

在客户端:

onConnect()
onDisconnect()
onMessageRecieved(char* data)
sendMessage(char* data)

没想到事情会这么复杂。

这是我正在使用的简单回显服务器:

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

class session
{
public:
  session(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }

  tcp::socket& socket()
  {
    return socket_;
  }

  void start()
  {
    socket_.async_read_some(boost::asio::buffer(data_, max_length),
        boost::bind(&session::handle_read, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }

  void handle_read(const boost::system::error_code& error,
      size_t bytes_transferred)
  {
    if (!error)
    {
      boost::asio::async_write(socket_,
          boost::asio::buffer(data_, bytes_transferred),
          boost::bind(&session::handle_write, this,
            boost::asio::placeholders::error));
    }
    else
    {
      delete this;
    }
  }

  void handle_write(const boost::system::error_code& error)
  {
    if (!error)
    {
      socket_.async_read_some(boost::asio::buffer(data_, max_length),
          boost::bind(&session::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
    else
    {
      delete this;
    }
  }

private:
  tcp::socket socket_;
  enum { max_length = 1024 };
  char data_[max_length];
};

class server
{
public:
  server(boost::asio::io_service& io_service, short port)
    : io_service_(io_service),
      acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
  {
    session* new_session = new session(io_service_);
    acceptor_.async_accept(new_session->socket(),
        boost::bind(&server::handle_accept, this, new_session,
          boost::asio::placeholders::error));
  }

  void handle_accept(session* new_session,
      const boost::system::error_code& error)
  {
    if (!error)
    {
      new_session->start();
      new_session = new session(io_service_);
      acceptor_.async_accept(new_session->socket(),
          boost::bind(&server::handle_accept, this, new_session,
            boost::asio::placeholders::error));
    }
    else
    {
      delete new_session;
    }
  }

private:
  boost::asio::io_service& io_service_;
  tcp::acceptor acceptor_;
};

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: async_tcp_echo_server <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    using namespace std; // For atoi.
    server s(io_service, atoi(argv[1]));

    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

我可以 telnet 进入这个服务器,一切都得到了回应。

现在我想将这段代码封装在onConnect()onDisconnect()onMessageReceived(char* data) 等中。类似于在 Node.js 中的处理方式!

有没有人有这方面的指点?

【问题讨论】:

  • 您要求为您编写大量代码。到目前为止,您尝试过什么?
  • 真的不多。我试图拦截 handle_read 和 handle_write 函数无济于事......我很感激你在这方面可能有的任何指针。将所有 ASIO 的东西打包成一个易于使用的类会非常好。我准备为此投入几个小时。
  • 我编写的代码正是这样做的,不幸的是它属于公司,不属于我的分享。不确定我能否给出一个合适的“答案”的简单概述,但我会尝试。
  • 不适合回答但是:使用boost::asio 以及异步函数(例如async_connect()', 'async_read()async_write())允许您提供一个处理程序(或回调)函数,该函数在操作时被调用完成。编写一个封装此功能的类相对简单,并且具有您使用此方法请求的必要On&lt;Action&gt; 函数。

标签: c++ boost asynchronous boost-asio


【解决方案1】:
  • 可以从handle_read 调用onMessageReceived()
  • 可以从start 调用onConnect()
  • onDisconnect() 可以在 session 类的析构函数中调用。

关于赏金问题:

io_service.run() 可以放在自己的线程中。

As per the documentation

对何时可以调用处理程序做出某些保证,特别是只能从当前对相应 io_service 对象调用 run() 的线程调用处理程序。

异步发送和接收可以由这个单线程处理。这简化了线程安全,因为所有回调将连续运行。这可能是使用 boost asio 最简单的方法了。

对于来自run() 线程外部的调用,您可以安排来自“外部线程”的回调(例如deadline_timer),以便立即调用以简化线程安全处理。例如

    boost::asio::deadline_timer timer(io_service);
    timer.expires_from_now(boost::posix_time::seconds(0));
    timer.async_wait(boost::bind(&MyClass::MyCallback, this, boost::asio::placeholders::error);

io_service 对象一有机会就会以线程安全的方式为您调用处理程序。这样,您的 asio 代码就可以表现得好像整个系统中只有一个线程。

如果需要或首选多个线程(例如,利用多核),您可以在多个线程上调用 run()。处理程序必须是可重入的。您可能还想为某些操作使用strand

否则,将应用常规线程安全规则。

【讨论】:

  • 您能否更明确地说明我将如何从“外部”线程安排deadline_timer?有这方面的文件吗?
  • 与后台线程中的“常规”同步调用相比,这些异步调用的优势是什么(也可以使用boost::asio 来完成?
  • 优点是阻塞调用与非阻塞调用。也可以决定采用不同的 I/O 策略。有多种方法,您也可以混合使用它们。我相信这本身就是一个问题。见kegel.com/c10k.html
  • 以TCP服务器为例。您需要在每个客户端上使用阻塞调用的线程。否则,如果您在套接字上读取客户端未写入所有其他客户端将被延迟。这就是你使用异步 i/o 的原因;在一个线程中服务多个客户端。如果您的应用程序只是一个服务器,您只需在主线程中调用阻塞 run()。让 boost asio 为您调度。如果您的应用程序做的比这更多,您还可以在将被阻止的不同线程上调用 asio run。 Asio 很灵活,你也可以在多个线程中调用它。
  • 是的,确实如此!我相信您刚刚向我阐明了我所缺少的内容——解释为什么这些异步调用很有用。结合 Evgeny Panasyuk 在上面的评论中提到的协程,设计实际上变得非常强大和灵活。我会得出结论,对于具有单个客户端的服务器,异步调用是一种可能的解决方案,但并不优于同步设计。对于具有多个客户端和多种通信状态的服务器,设计变得非常强大和优越。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-20
  • 1970-01-01
  • 2020-04-24
  • 1970-01-01
  • 2015-06-26
  • 2012-08-07
  • 2014-10-15
相关资源
最近更新 更多