12 Mart 2018 Pazartesi

asio tcp acceptor Sınıfı

Giriş
Şu satırı dahil ederiz.
#include <boost/asio.hpp>
Kolay kullanım için şu satırı dahil ederiz.
using boost::asio::ip::tcp;
Kalıtım şöyledir.
acceptor -> basic_socket_acceptor -> socket_base
Constructor - enpoint
Eğer bind işlemini gerçekleşmez ise exception fırlatır. Bu çağrı yerine
open + bind çağrıları yapılabilir.

Şöyle yaparız.
tcp::endpoint ep (tcp::v4(), 1234);
tcp::acceptor acceptor(ios, ep);
Şöyle yaparız.
tcp::acceptor acceptor(ios,tcp::enpoint(tcp::v4(),134);
Bu sınıfı şöyle sarmalarız.
listener::listener (io_service& io_service, std::string ip, short port)
 :acceptor(io_service, tcp::endpoint(ip::address_v4::from_string(ip), port)),
  socket(io_service)
{
  logger << ("Network bound" << acceptor.local_endpoint().address().to_string()
          << acceptor.local_endpoint().port();

  start_accepting();
  
}
İçinde bir tane socket barındırır.
class listener
{

private:
  boost::asio::ip::tcp::acceptor acceptor;
  boost::asio::ip::tcp::socket socket;
};
accept metodu - tcp::socket
Şöyle yaparız.
tcp::socket sock = ...;
acceptor.accept (sock);
accept çağrısından sonra sock nesnesini yaşaması gerekir. Eğer sock nesnesi bir scope içinde yaratılırsa ve scope'tan çıkarken otomatik olarak yok edilirse yanlış olur.
tcp::socket sock (...);           
acceptor.accept (sock);
...
~socket(); 
accept metodu - tcp::iostream
iostream nesnesi içinde tcp::socket barındırır. Şöyle yaparız.
tcp::iostream ss;
boost::system::error_code ec;
acceptor.accept (*ss.rdbuf(), ec);
async_accept metodu - tcp::socket
Bu çağrıdan önce acceptor nesnesinin bind() ve listen () metodları çağrılmış olmalıdır.
Acceptor'a boost::asio::ip::tcp tipinde bir socket verilir.
acceptor.async_accept(sock,
        boost::bind(&accept_handler, asio::placeholders::error));
İstersek lambda kullanabiliriz. Şöyle yaparız.
acceptor.async_accept(socket, [this](const system::error_code& e){
  ...
})
async_accept metodu - tcp::iostream
iostream nesnesi içinde tcp::socket barındırır.  Bu çağrıdan önce acceptor nesnesinin bind() ve listen () metodları çağrılmış olmalıdır.

Elimizde bir tcp::iostream sınıfı olsun.
tcp::iostream ss;
Şöyle yaparız.
acceptor.async_accept(*ss.rdbuf(),
 boost::bind(&Server::handleAccept, this,
              boost::asio::placeholders::error, boost::ref( acceptor ) ) );
bind metodu - endpoint
Şöyle yaparız.
tcp::endpoint ep (ip::tcp::v4(), 1500);
acceptor.bind(ep);
close metodu
Şöyle yaparız.
acceptor.close();
get_io_service metodu
Şöyle yaparız.
acceptor.get_io_service();
is_open metodu
Şöyle yaparız.
if(acceptor.is_open ()) {...}
listen metodu
Şöyle yaparız.
acceptor.listen ();
open metodu - protocol
Şöyle yaparız.
tcp::endpoint ep (tcp::v4(), 1500);
acceptor.open (ep.protocol());
set_option metodu
Şöyle yaparız.
acceptor.set_option (tcp::acceptor::reuse_address(true));
Şöyle yaparız
acceptor.set_option(boost::asio::socket_base::reuse_address(true));
Diğer

Handler Nasıl Yazılır
Hem iostream hem de socket kullanan handler aynıdır. Parametreleri error ve isteğe bağlı olarak acceptor'ın kendisi olabilir. Hata kodunu kontrol etmek gerekir.
void handleAccept(const error_code& error, acceptor& acceptor)
{
  if(error)
    std::cout << error.message() << "(" << error.value() << ")" << std::endl;
}
1. acceptor içindeki socket ile yeni bir tcp_client nesnesi oluşturulur.
void start_accepting (){
  acceptor.async_accept(socket, [this](boost::system::error_code ec)
  {
    if (!ec)
    {
      logger <<"Connection" << socket.remote_endpoint().address().to_string());
      std::make_shared<tcp_client>(socket)->start_receiving();
    }
    else
    {
      acceptor.close();
    }

    start_accepting();
  });
}
2. tcp_client nesnesi std::enabled_shared_from_this'ten kalıtan bir sınıftır. Şöyle yaparız.
class tcp_client : public std::enable_shared_from_this<tcp_client>
{
public:
  tcp_client(tcp::socket socket) : socket_(std::move(socket)) {
    ...
  }

  void start_receiving() {...}

private:
   
  tcp::socket socket_;
   
};
Sarmalanan sınıf kendi içinde socket.asyncXXX (buffer, bind (...,std::shared_from_this,...)); çağrıları yapmaya devam ettikçe heap'te yaşamaya devam eder.

3. Benzer şekilde şöyle yaparız. Tek fark socket nesnesi acceptor içinde değil, client nesnesi içine yaşar.
std::shared_ptr<Connection> createConnection(){
  std::shared_ptr<Connection> newConnection = Connection::create(m_ioService);
  m_acceptor.accept(newConnection->getSocket());
  return newConnection;
}
4. Benzer şekilde şöyle yaparız. Socket nesnesi acceptor içinde dğil client nesnesi içinde yaşar. acceptor connection nesnesine run() çağrısı yapar ve kendini tekrar başlatır.
void begin_accepting(asio::ip::tcp::acceptor& acceptor)
{
  auto candidate = std::make_shared<connection>(acceptor.get_io_service());
  acceptor.async_accept(candidate->socket_, [candidate, &acceptor](auto const& ec)
  {
    if (not ec) {
      candidate->run();
      begin_accepting(acceptor);
    }
  });
}
Örnek
Şöyle yaparız.
class Server
{
public:
  Server(boost::asio::io_service& p_service, unsigned p_port) :
    m_service(p_service),
    m_acc(m_service, boost::asio::ip::tcp::endpoint( asio::ip::tcp::v4(), p_port ) ),
    m_session( std::make_shared< Session >() )
  {
    StartListen();
  }

private:
  void StartListen()
  {
    m_acc.async_accept(
      m_session->tcp_stream().rdbuf(),
      std::bind(&Server::AcceptHandler, this, _1) );
  }

  void AcceptHandler(const boost::system::error_code& p_error)
  {
    if( !p_error )
    {
      auto ses = std::make_shared< Session >();
      std::swap( ses, m_session );
      ses->InitLifeCircle(); // Start whatever logic you needed.
      StartListen();
    }
  }

private:
  boost::asio::io_service&        m_service;
  boost::asio::ip::tcp::acceptor  m_acc;
  std::shared_ptr< Session >      m_session;
};


Hiç yorum yok:

Yorum Gönder