libboost ASIO。简单的异步客户端服务器

发布于 2024-11-27 19:22:18 字数 3397 浏览 1 评论 0原文

我正在尝试在 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;
}

我可以远程登录到该服务器,并且所有内容都会得到回显。

现在我想将此代码封装在 onConnect()onDisconnect()onMessageReceived(char* data) 等中。遵循 Node.js 中的完成方式!

有人在这方面有任何指示吗?

I'm trying to implement a simple client/server in ASIO.

I'd like the following on the serverside:

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

and on the client side:

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

I didn't realise things would be so complicated.

Here's the simple echo server which I'm working off of:

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

I can telnet into this server and everything is echoed.

Now I'd like to wrap up this code in onConnect(), onDisconnect(), onMessageReceived(char* data), etc. Similar to the way things are done in Node.js!

Has anyone got any pointers in this regard?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

空名 2024-12-04 19:22:18
  • 可以从handle_read调用onMessageReceived()
  • onConnect() 可以从 start 调用。
  • onDisconnect() 可以在 session 类的析构函数中调用。

对于赏金问题:

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

根据文档

对何时可以调用处理程序做出了某些保证,特别是处理程序只能从当前正在相应 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 对于某些操作。

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

  • onMessageReceived() can be called from handle_read.
  • onConnect() can be called from start.
  • onDisconnect() can be called in the destructor of the session class.

For the bounty questions:

The io_service.run() can be placed in its own thread.

As per the documentation

Certain guarantees are made on when the handler may be invoked, in particular that a handler can only be invoked from a thread that is currently calling run() on the corresponding io_service object.

Asynchronous sending and receiving can be handled by this single thread. This simplifies thread safety because all the callbacks will be running in succession. This is probably the simplest way of using boost asio.

For calls coming from outside of the run() thread, you can schedule a callback (e.g. deadline_timer), from the 'outside thread' for immediate calling to simplify your thread safety handling. e.g.

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

The io_service object will call the handler for you in a thread-safe fashion as soon as it has a chance. This way, your asio code can behave as if there was only a single thread in the entire system.

If multiple threads are required or preferred (e.g. Take advantage of multi-core) you may call run() on multiple thread. Handlers will have to be re-entrant. You may also want to use a strand for certain operations.

Otherwise, regular thread safety rules applies.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文