libboost ASIO.Простой асинхронный клиентский сервер - PullRequest
6 голосов
/ 04 августа 2011

Я пытаюсь реализовать простой клиент / сервер в 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!

Кто-нибудь есть какие-либо указатели на этот счет?

1 Ответ

3 голосов
/ 11 апреля 2013
  • onMessageReceived() можно вызывать с handle_read.
  • onConnect() можно вызывать с start.
  • onDisconnect() можно вызвать в деструкторе класса session.

По вопросам щедрости:

io_service.run() можно поместить в собственный поток.

Согласно документации

Некоторые гарантии даются, когда обработчик может быть вызван, в частности, что обработчик может быть вызван только из потока, который в данный момент вызывает run () для соответствующего объекта io_service.

Асинхронная отправка и получение могут обрабатываться этим единственным потоком. Это упрощает безопасность потока, потому что все обратные вызовы будут выполняться подряд. Это, наверное, самый простой способ использования 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() в нескольких потоках. Обработчики должны быть повторно входящими. Вы также можете использовать прядь для определенных операций.

В противном случае применяются обычные правила безопасности нитей.

...