29 Haziran 2016 Çarşamba

boost asio

Kullanmak
asio kütüphanesini kullanmak için
#include <boost/asio.hpp>
yapmak yeterli. Linklemek gerekmiyor. asio kütüphanesi, alt seviye socket erişimi sağlar. Ancak SSL, SMPT, POP3 socket gibi protokol gerçekleştirimi sağlamaz. Bu tür soketler için poco kullanılabilir.

İleriki C++ sürümlerinde asio yerine Network Technical Specification kullanılabilir. Kapsamı asio kadar geniş değil. SSL, signals vs. olmayacak gibi görünüyor.

Microsoft asenkron programlama için bir sürü seçenek sunuyor. Bunlar şöyle

1. Asynchronous Programming Model (APM)
2. Event-based Asynchronous Pattern (EAP)
3. Task-based Asynchronous Pattern (TAP)

asio bence EAP modeline benziyor.

asio namespace
Bu namespace altındaki önemli sınıflar buffer, streambuf, io_service sınıfları

Önemli Metodlar
asio namespace altında önemli metodlar var.

async_read
async_read metodu yazısına taşıdım.

async_write
Temel olarak metod imzası şöyle
async_write(socket, buffer)

Örnek
std::string const  msg = ...;
    
    
boost::asio::async_write(
       socket,
       boost::asio::buffer(msg.c_str(),msg.length()),
       boost::bind(&MyClass::handle_write,this,boost::asio::placeholders::error));
Yazma işlemi bitince completion callback çağrılır.
void MyClass::handle_write(const boost::system::error_code & error)
{
  if (error) {
    std::cout << "Write complete with errors !!!\n";
  }
  else {
    std::cout << "Write complete with no errors\n";
  }        
}
read_until metodu
Bu metodun tam 8 tane overload edilmiş hali mevcut. Alltaki soketin read_some metodunu kullanır. Anladığım kadarıyla sadece tcp soketi için kullanılabilir. Şöyle yaparız.
boost::asio::streambuf b;
std::size_t bytes_transferred = boost::asio::read_until(socket, b,delim);

error_code kullanan hali
Şöyle yaparız
boost::system::error_code err;

boost::asio::streambuf buf;

boost::asio::read_until(socket, streamBuf, '\0', err);
if (err)
{...}
write metodu - string
Senkron olarak veriyi gönderir.
boost::asio::write(sock,buffer("hello world"));
Şöyle yaparız.
std::string message="...";
boost::system::error_code ignored_error;
//write the message
boost::asio::write(sock, boost::asio::buffer(message),ignored_error);
write metodu - byte array
Şöyle yaparız.
std::size_t bytes_out=boost::asio::write(sock, boost::asio::buffer(msg, len),
            boost::asio::transfer_all(), ignored_error);
assert(bytes_out==160000);
write metodu - struct
Şöyle yaparız.

struct packet{
...
};
std::vector<packet> sendpacket;
sendpacket.push_back(...);

socket.send(boost::asio::buffer(sendpacket));
streambuf sınıfı
streambuf Sınıfı yazısına taşıdım.

Buffer Sınıfı
asio'da MutableBuffer ve ConstBuffer kavramları var. Buffer verilen belleği yönetmez. Dolayısıyla bir bellek alanı yaratmak gerekir. Eğer buffer boyutunu önceden bilmiyorsak unique_ptr
std::unique_ptr<char[]> buffer(new char [size]);
some_io_function(buffer.get(), size); // get() returnes raw pointer
veya shared_ptr
std::shared_ptr<char> buffer(new char[size], std::default_delete<char[]>());
veya vector
std::vector<char> buffer(size);
some_io_function(buffer.data(), buffer.size()); // data() returnes raw pointer
ile dinamik bellek alanı yaratılır.

Eğer bellek büyüklüğünü önceden biliyorsak en güzeli sabit bir array kullanmak.

const_buffer
Const buffer şöyle yaratılır ve sockete yazma için kullanılır.
boost::asio::buffer(data)

String yazmak için şöyle yaparızasync_read_until.
std::string str = ...;
boost::asio::async_write(socket, boost::asio::buffer(str, str.length()), handler);
mutable_buffer
Mutable buffer socketten okuma için kullanılır ve şöyle yaratılır.
boost::asio::buffer(data, size)

Sabit array'i doldurmak için şöyle yapılır.
uint8_t data[8000];
sock.async_receive_from(boost::asio::buffer(data, 8000),...);
Heap'te yaratılmış array'i doldurmak için şöyle yapılır.
char * data = new char[200];
boost::asio::async_read(_socket, boost::asio::buffer(data, 200), 
vector'ü doldurmak için şöyle yapılır.
std::vector<char> data(128);
bytes_transferred = sock.receive(boost::asio::buffer(data));
mutable_buffer Sınıfı
constructor
Şöyle yaparız.

std::vector<char> data = ...;
boost::asio::mutable_buffer buf(&data, data.size());
null_buffer Sınıfı
Bu özel bir sınıf, sockete gerçek bir buffer vermek yerine tam okuma işlem esnasında buffer yaratılmasını sağlar. Tcp ile pek faydalı değil belki ancak udp kullanırken tam boyutunu bilemediğimiz paketleri okumak için kullanılabilir.
ip::tcp::socket socket(my_io_service);
...
socket.non_blocking(true);
...
socket.async_read_some(null_buffers(), read_handler);
...
void read_handler(boost::system::error_code ec)
{
  if (!ec)
  {
    std::vector<char> buf(socket.available());
    socket.read_some(buffer(buf));
  }
}


asio::ssl namespace
context sınıfı
boost asio ssl context Sınıfı yazısına taşıdım.

stream sınıfı
Şu satırı dahil ederiz.
#include <boost/asio/ssl.hpp>
Sınıf bir template. Altta OpenSSL kütüphanesini kullanır.

Constructor
Şöyle yaparız.
boost::asio::io_service io_service;
boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv1);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(io_service, ctx);
socket sınıfı

async_connect metodu
Şöyle yaparız
boost::asio::async_connect(socket->lowest_layer(), endpoint_iterator,
        boost::bind(&..., this, boost::asio::placeholders::error));
Handler metodu şöyledir.
void TLSSocket::on_connect(const boost::system::error_code& error){
  if(!error) {
    try {
    socket->async_handshake(boost::asio::ssl::stream_base::handshake_type::client,
                   boost::bind(&..., this, boost::asio::placeholders::error));
    } catch (std::exception ex){
      std::cout << "Handshaking error: " << ex.what() << "\n";
    }
  } else {
    std::cout << "Connect error: " << error.message() << "\n";
  }
}
Handshake handler metodu şöyledir.


void handle_handshake(const boost::system::error_code& err)
{
  if (!err)
  {
    boost::asio::async_write(socket,requestBuf,handler);

  }
}
async_handshake metodu
Şöyle yaparız.
socket.async_handshake(boost::asio::ssl::stream_base::handshake_type::client,
    boost::bind(&..., this, boost::asio::placeholders::error));
Handler şöyledir.
void TLSSocket::on_handshake(const boost::system::error_code& error){
  if(!error){
    ...
  } else {
    std::cout << "Handshaking error: " << error.message() << "\n";
  }
}
lowest_layer metodu
basic_socket'i döndürür. Socketi kapatmak için şöyle yaparız.
void TLSSocket::disconnect() {
  socket.lowest_layer().cancel();}
next_layer metodu
tcp socketini döndürür.

read_some metodu
Şöyle yaparız.
boost::array<char, 128> buffer;
boost::system::error_code error;
size_t len = socket->read_some(boost::asio::buffer(buffer), error);

if (error == boost::asio::error::eof)
  break;
else if (error)
  throw boost::system::system_error(error);

std::cout.write(buffer.data(), len);
set_verify_callback metodu
Context ile sertifika bir şekilde doğrulanıyor. Doğrulama metodunda set_verify_callback kullanmak için şöyle yaparız. Bu çağrıdan önce set_verify_mode çağrısı yapılabilir.
socket.set_verify_callback(boost::bind(&..., this, _1, _2));
Handler kodlamak için şöyle yaparız.
bool TLSSocket::on_verify_certificate(bool verified,
  boost::asio::ssl::verify_context &ctx) {
    char subject_name[256];
    X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
    X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
    std::cout << "Verifying " << subject_name << "\n";

    return verified;
}
set_verify_mode metodu
Şöyle yaparız.
socket.set_verify_mode(asio::ssl::verify_fail_if_no_peer_cert);
Şöyle yaparız.
socket.set_verify_mode(boost::asio::ssl::verify_peer);
write metodu
Şöyle yaparız
boost::asio::streambuf requestBuff;
std::ostream request_stream(&requestBuff);
...

boost::asio::write(socket, requestBuff);
asio::ip::udp namespace
asio udp yazısına taşıdım.

asio::ip::tcp namespace
asio tcp yazısına taşıdım.

asio::serial_port
asio serial_port yazısına taşıdım.
asio::io_service namespace
Bu namespace altındaki önemli sınıf strand.

strand Sınıfı
stand Sınıfı yazısına taşıdım