Boost.Asio를 이용한 네트웍
       프로그래밍


            최흥배
     Twitter : @jacking75
Visual C++ MVP 2008 ~ 2011
http://devwith.com/
http://jacking.tistory.com/




https://twitter.com/jacking75
Boost.Asio ?
Boost.Asio란?
• Boost 라이브러리의 일부. 주로 네트웍 프로그래밍에 사용

• Asynchronous I/O (비동기 입출력).

• I/O와 같이 시간이 걸리는 처리를 OS의 비동기 기능과 스레
  드를 사용하여 처리.

• 파일 입출력이나 시리얼 입출력, 또는 일반적인 비동기 프로
  그래밍에서도 사용.

• 멀티 플랫폼 지원.
OS 플랫폼 별 구현
• Linux Kernel 2.4
  select를 사용하므로 FD_SIZE 크기를 넘지 못함

• Linux Kerner 2.6
  epoll을 사용

• FreeBSD, Mac OS X
  Kqueue를 사용

• Solaris
  /dev/poll을 사용

• Windows(Windows 2000 이후)
  Overlapped I/O와 I/O Completion을 사용
Boost.Asio의 주요 클래스
• boost::asio::io_service 가장 중요

• ip::tcp::socket (http에도 사용)

• ip::udp::socket

• ip::icmp::socket (ping 등에 사용)

• ssl::context (Open SSL이 필요)

• serial_port

• boost::deadline_timer
믿을 수 있나?
• 신뢰성이 높음.

• (아마도)차기 C++ 표준에 들어갈 확률이 높음...

• 한국의 몇몇 온라인 게임에서 이미 사용 중,
  또 한국의 모 대형 IT 회사의 내부 네트웍 라이브러리로
  Boost.Asio를 사용하고 있다고 함 .
성능?
편의성?
직접 만들기….
Boost 빌드?




   혹은 직접 빌드 하기…..
Demo !!!
Demo : Asio를 동기 모드로 사용




Tutotial_001
int main()
{
    asio::io_service io_service;
    tcp::socket socket(io_service);

   boost::system::error_code error;
   auto endpoint =
tcp::endpoint(asio::ip::address::from_string("127.0.0.1"),
31400);

    socket.connect( endpoint, error );

  if (error)
  {
      std::cout << "connect 실패 : "
<< error.message() << std::endl;
  }
  else
  {
      std::cout << "connected" << std::endl;
  }

    return 0;
}
Demo : Asio를 비동기 모드로 사용




Tutotial_002
void connect()
{
  auto endpoint =
tcp::endpoint(asio::ip::address::from_string("127.0.0.1"),
31400);

    socket_.async_connect(
              endpoint,
              boost::bind( &Client::on_connect,
                        this,
                        asio::placeholders::error) );
}

void on_connect(const boost::system::error_code& error)
{
   if (error)
   {
      std::cout << "connect failed : " << error.message()
<< std::endl;
   }
   else
   {
      std::cout << "connected" << std::endl;
   }
}
int main()
{
    asio::io_service io_service;
    Client client(io_service);

    client.connect();

    io_service.run();
}
Boost application performance
using asynchronous I/O
http://www.ibm.com/developerworks/linux/library/l-async/
Typical flow of the synchronous blocking I/O model
Typical flow of the synchronous non-blocking I/O model
Typical flow of the asynchronous blocking I/O model (select)
Typical flow of the asynchronous non-blocking I/O model
Proactor and Boost.Asio
Boost.Asio와 IOCP

                                  g_hIocp =
                                  CreateIoCompletionPort(INVALID_HANDLE_VA
                                  LUE, NULL, 0, 0);


asio::io_service io_service;
                                  client = accept(server, (struct sockaddr*)&addr,
                                  &addrlen);
tcp::socket socket(io_service);
                                  g_hIocp =
io_service.run();
                                  CreateIoCompletionPort((HANDLE)client,
                                  g_hIocp, (DWORD)con, 0);


                                  GetQueuedCompletionStatus(g_hIocp,
                                  &readbytes, &dwCompKey, (LPOVERLAPPED
                                  *)&pOverlap, INFINITE);
Asio의 비동기 모델 - 스레드 모델

                애플리케이션
                                                          callback 함수 오브젝트


                                            run()
          Boost 소켓
                  io_service
                                                              int main()
                          I/O                                 {
                                                                  asio::io_service io_service;
                                                                  Client client(io_service);
                     OS
                                                                  client.connect();

                                                                  io_service.run();
                                                              }


참조 : http://d.hatena.ne.jp/Softgels/20090304/1236123151
Asio의 비동기 모델 - 멀티 스레드 모델
                    Boss 스레드
                                 만들고, 종료까지 기다림


                 Worker 스레드
               Worker 스레드                                         callback 함수 오브젝트
             Worker 스레드
                                                          run()

               Boost 소켓
                                                                     for( INT32 i = 0; i < 3; ++i )
                         io_service
                                                                     {
                              I/O                                         m_IoWorkThreadList.emplace_back
                                                                          (
                                                                             std::thread( boost::bind(
                          OS                                                         &boost::asio::io_service::run,
                                                                                     &m_IOService)
                                                                                   )
                                                                          );
                                                                     }
참조 : http://d.hatena.ne.jp/Softgels/20090304/1236123151
io_service 클래스
 소켓, 타이머 등 비동기 입출력 이벤트를 디스패치하는 클래스

 멤버 함수
  size_t run();
  size_t run( boost::system::error_code& e );
  size_t run_one();
  size_t run_one( boost::system::error_code& e );
  size_t pool();
  size_t pool( boost::system::error_code& e );
  size_t pool_one();
  size_t pool_one( boost::system::error_code& e );

 run 함수는 모든 이벤트가 처리될 때까지 블럭된다.

 run_one 함수는 하나의 이벤트가 처리될 때까지 블럭된다.

 poll 함수는 블럭하지 않으면서 처리할 이벤트를 모두 처리하고 종료한다.

 poll_one 함수는 블럭하지 않으면서 1개((또는 0개) 이벤트를 처리하고 종료한다.

 모두 실행한 이벤트 개수를 반환한다.
 정지 함수
   void stop();
   void reset();

 run이나 poll 함수의 처리 루프를 정지할 때 stop 함수를 사용한다.

 stop 사용 후에 다시 run이나 poll을 사용하면 에러가 발생. 이때는 reset 함수를 사용해야 한다.
io_service는 Asio의 시작이자 끝!!!
TCP/UDP 통신
 TCP/IP
  boost::asio::ip::tcp

 UDP/IP
  boost::asio::ip::udp

 protocol
  v4(), v6()

 endpoint
  접속 주소를 지정
  INADDR_ANY로 포트번호만 지정하던가 IP와 포트번호를 지정할 수 있다.

 resolver
  호스트 이름에서 IP 주소로 변환시키는 클래스.

 acceptor
  클라이언트로부터 TCP 접속을 받아 들이기 위해서 서버에서 사용하는 클래스.
  accpet 처리에는 동기와 비동기 두 개의 버전이 있다.
buffer 클래스
 고정 사이즈 버퍼
  배열, boost::array, std::vector, std::string에 buffer 함수를 적용하며 기본 입출력에 넘길 수 있는
  버퍼 오브젝트가 된다.

  size_t 인수로 사이즈를 지정하면 모두 바이트 단위로 지정한다.



 함수 버퍼
  const_buffer 또는 mutable_buffer를 저장한 컨테이너는 기본 입출력 함수에 넘길 수 있다.

  read 계열 함수에 넘긴 경우 모든 버퍼의 내용을 연결된 것으로 저장된다.


 스트림 버퍼
  STL의 스트림 버퍼로서 사용할 수 있으며 기본 입출력 버퍼이다.
기본 입출력
 동기 읽기
  완료 조건을 만족하던가 에러가 발생할 때까지 블럭.
  read_until에서는 구분 문자 delim을 읽을 때까지 블럭한다.
  ErrorHandler는 아래의 시그네쳐를 가진 함수 오브젝트를 지정할 수 있다.
  void error_han( const boost::system::error_code& e );

 비동기 읽기
  블럭킹 하지 않고 버퍼에 데이터를 읽어 들인다. 완료하던가 에러가 발생하면 소켓에 연결된
  io_service의 run 함수에서 Handler 함수 오브젝트를 호출한다.
  Handler에는 아래의 시그네처를 가진 함수 오브젝트를 지정할 수 있다.
  void condition( const boost::system::error_code& e, size_t bytes_transferred );


 동기 쓰기
  버퍼로 부터 데이터를 쓴다.
  완료 또는 에러가 발생할 때까지 블럭킹 한다.

 비동기 쓰기
  블럭킹 하지 않고 버퍼에서 데이터를 쓴다.
  완료 또는 에러가 발생하면 소켓에 연결된 io_service의 run 함수로부터 Handler 함수 오브젝트
  가 호출된다.
  Handler에는 아래의 시그네처를 가진 함수 오브젝트를 지정할 수 있다.
  void condition( const boost::system::error_code& e, size_t bytes_transferred );
error_code 클래스
 시스템 의존 에러코드를 랩핑한 클래스.

 bool 타입으로 평가하면 에러 때는 true, 정상일 때는 false.

 message 및 wmessage 멤버 함수를 사용하면 상세한 에러를 문자열로 얻을 수 있다.

             class error_code
             {
             public:
               error_code();
               error_code( value_type val, error_category cat );
               operator unspecified_bool_type() const;
               bool operator() const;
               value_type value() const;
               error_category category() const;
               int to_error() const;
               std::string message() const;
               wstring_t wmessage() const;
             };
Demo : 간단한 채팅




Tutotial_Chat   http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/examples.html#boost_asio.examples.allocation
Demo : io_service를 포함하는 설계




Tutorial_IOServicePattern
Demo : 간단한 백그라운드 처리




Tutorial_SimpleBackground
Demo : poll




Tutorial_poll
 run()은 이벤트 큐가 없을 때까지 대기,

 poll()은 준비가 끝난 이벤트를 실행.

 준비가 끝난 이벤트란 핸들러를 실행할 수 있는 준비가 끝난 것을 말한다.
  그러므로 타이머나 IO 완료 통지는 poll()로는 기다릴 수 없다.

 poll()에서 대기하지 않은 핸들러는 poll()이 끝난 후에 run()이나 run_one()
  등으로 처리하면 된다.
Demo : 워커 스레드 패턴




Tutorial_WorkerThreadPattern
Demo : 이벤트 디스패치 스레드




Tutorial_EventDispatch
Io_service::work
비동기 처리를 등록하기 전에 io_servive::run()을 호출하면 io 작업이 완료되
어 버리고 스레드는 종료된다.

이것을 방지하기 위해 io_service::work를 사용한다.
Io_service::work를 사용하면 work가 파괴될 때까지 ioLservice::run()은 종료
하지 않는다




   boost::shared_ptr<boost::asio::io_service::work> work_;

   work_.reset(new boost::asio::io_service::work(io_service_));
strand 클래스
class strand
{
public:
  strand( io_service& io );

     template< typename Handler >
      unspecified wrap( Handler han );
};



핸들러 함수 han을 랩핑해서 동일의 strand로 랩핑된 핸들러가 복수의 스레드에서 병렬로 실행되
지 않도록 동기화된 핸들러 함수로 바꾸어준다.

strandobj.wrap(han)의 결과는 그대로 기본 입출력 함수나 타이머 등의 핸들러 함수로서 사용할 수
있다.


                              async_op_1(..., s.wrap(a));
                              async_op_2(..., s.wrap(b));
Demo : Windows에서 파일을 비동
   기로 읽기




Tutorial_AsyncFileRead
Demo :
   boost::asio::deadline_timer의
   cancel()



Tutorial_TimerCancel
Demo :
   boost::asio::deadline_timer의
   cancel_one()



Tutorial_TimerCancelOne
Demo : post와 dispatch의 차이




Tutorial_post_dispatch
 dispatch()에서 호출한 함수는 dispatch()를 호출한 곳과 같은 스
  레드에서 호출된다.
  그러나 post()의 경우는 다른 스레드에서 호출될 수 있다.

 dispatch()는 호출한 곳의 함수가 비동기로 실행되고 있는 경우
  비동기가 아닌 직접 함수를 호출하거나, post()와 같이 Queue에
  등록하여 비동기로 실행한다.

 dispatch()의 경우 핸들러 함수 콜스택이 깊어질 수 있으나(재귀
  호출을 하면) post()는 호출한 핸들러 함수에서 제어 흐름이 일단
  io_service 내의 메시지 루프로 돌아간 후 post 된 핸들러 함수가
  호출되므로 콜 스택이 깊어지지 않는다.

 dispatch()의 경우 컨텍스트가 바뀌지 않으므로 또 내용이 관련
  된 핸들러가 연속해서 불려지므로 참조 국소성이 유지되지 않을
  까 생각한다.
참고 자료
참고
 Boost 라이브러리 공식 사이트
  http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio.html

 boostpro 사이트
  http://www.boostpro.com/download/

 Boost 라이브러리 직접 빌드하기
  http://jacking.tistory.com/986

 asio C++ Library
  http://think-async.com/Asio

 Boost e-Book
  http://en.highscore.de/cpp/boost/
resolver를 사용한 도메인 네임 to IP 어
드레스
      #include <boost/asio.hpp>
      #include <boost/bind.hpp>
      #include <iostream>

      namespace asio = boost::asio;
      namespace ip = asio::ip;

      class Client
      {
        asio::io_service& io_service_;
        ip::tcp::resolver resolver_;

      public:
       Client(asio::io_service& io_service)
       : io_service_(io_service), resolver_(io_service)
       {
          ip::tcp::resolver::query query("google.com", "http");
          resolver_.async_resolve(query, boost::bind( &Client::on_resolve,
                                                      this,
                                                      asio::placeholders::error,
                                                      asio::placeholders::iterator)
                       );
       }
private:
  void on_resolve(const boost::system::error_code& error,
  ip::tcp::resolver::iterator endpoint_iterator)
  {
    if (error) {
       std::cout << error.message() << std::endl;
    }
    else
    {
       ip::tcp::resolver::iterator end;

             for (; endpoint_iterator != end; ++endpoint_iterator) {
               std::cout << endpoint_iterator->endpoint().address().to_string() << std::endl;
             }
         }
     }
};


int main()
{
  asio::io_service io_service;

     Client client(io_service);

     io_service.run();
}
자신의 Host 이름 얻기
      #include <boost/asio.hpp>
      #include <iostream>

      namespace asio = boost::asio;

      int main()
      {
        const std::string host_name = asio::ip::host_name();
        std::cout << "hostname: " << host_name << std::endl;
      }
비동기 핸들러 디버그 지원
1.47 버전부터 지원하는 것으로 BOOST_ASIO_ENABLE_HANDLER_TRACKING 라는 비동기 핸들러 디
버그 지원 매크로를 사용하면 consol에 로그가 찍힌다.

#define BOOST_ASIO_ENABLE_HANDLER_TRACKING

@asio|1310697299.008735|0*1|socket@0012FE5C.async_send
@asio|1310697299.008735|0*2|socket@0012FE5C.async_receive
@asio|1310697299.008735|>1|ec=system:0,bytes_transferred=8
request : request

@asio|1310697299.008735|<1|
@asio|1310697299.039985|>2|ec=system:0,bytes_transferred=9
response : response

@asio|1310697299.039985|<2|
@asio|1310697300.039985|0*3|socket@0012FE5C.async_send
@asio|1310697300.039985|0*4|socket@0012FE5C.async_receive
@asio|1310697300.039985|>3|ec=system:0,bytes_transferred=8
request : request

@asio|1310697300.039985|<3|
@asio|1310697300.039985|>4|ec=system:0,bytes_transferred=9
response : response

@asio|1310697300.039985|<4|
@asio|1310697301.039985|0*5|socket@0012FE5C.async_send
@asio|1310697301.039985|0*6|socket@0012FE5C.async_receive
@asio|1310697301.039985|>5|ec=system:0,bytes_transferred=8
request : request
...
출력 양식
<tag>|<timestamp>|<action>|<description>


<tag>는 보통 @asio로 된다. 이 행은 트랙킹 메시지라는 것을 알려준다.

<timestamp>는 유닉스 시간이다. 1970년 1월1일부터의 시간. 초와 밀리 초
가 출력된다.

<action> 규칙은 아래와 같음
>n : 프로그램 핸들러 수
<n : 종료한 핸들러 수
!n : 예외로 종료한 핸들러 수
~n : 호출된 적이 없이 파괴된 핸들러 수
n*m : 새롭게 작성한 비동기 조작 수 n과 완료 대기 핸들러 수 m
n : 그 외 조작을 한 핸들러 수. close나 cancel 등.

<description>는 동기 or 비동기 조작이 실행되었는가를 출력한다.
형식은 「<object-type>@<pointer>.<operation>」. 핸들러 인수는 콤마로 구
분된 리스트로서 출력한다.
완료 조건

transfer_all_t transfer_all();
transfer_at_least_t transfer_at_least( size_t min );

기본 입출력 함수의 CompletionCondition 인수로 넘길 수 있는 오브젝트
예를들면 transfer_all() 함수를 넘기면 버퍼 모두를 사용할 때까지 입출력이 진행된다.
transfer_at_least(m)를 넘기면 최저 m 바이트 이상의 쓰기가 진행될 때까지는 입출력이 진행된다.

보통 아래와 같은 시그네쳐를 가진 함수 오브젝트를 넘길 때 사용한다. true를 넘기면 입출력 완료
로서 좋다는 것을 표시한다.
bool condition( const boost::system::error_code& e, size_t bytes_transferred );
boost::array<char, 128> buf;
boost::system::error_code ec;
std::size_t n = boost::asio::read( sock, boost::asio::buffer(buf), boost::asio::transfer_all(), ec );
if (ec)
{
// An error occurred.
}
else
{
// n == 128
}
boost::array<char, 128> buf;
boost::system::error_code ec;
std::size_t n = boost::asio::read( sock, boost::asio::buffer(buf), boost::asio::transfer_at_least(64), ec );
if (ec)
{
// An error occurred.
}
else
{
// n >= 64 && n <= 128
}
boost::asio::async_read( sock, boost::asio::buffer(data, size), boost::asio::transfer_at_least(32), handler );
BSD Socket - Boost.Asio
             BSD Socket API                                         Boost.Asio
 socket descriptor - int (POSIX) or SOCKET   For TCP: ip::tcp::socket, ip::tcp::acceptor
 (Windows)                                   For UDP: ip::udp::socket

                                             basic_socket, basic_stream_socket,
                                             basic_datagram_socket, basic_raw_socket


 in_addr, in6_addr                           ip::address, ip::address_v4, ip::address_v6

 sockaddr_in, sockaddr_in6                   For TCP: ip::tcp::endpoint

                                             For UDP: ip::udp::endpoint

                                             ip::basic_endpoint
 accept()                                    For TCP: ip::tcp::acceptor::accept()

                                             basic_socket_acceptor::accept()
 bind()                                      For TCP: ip::tcp::acceptor::bind(), ip::tcp::socket::bind()

                                             For UDP: ip::udp::socket::bind()

                                             basic_socket::bind()
BSD Socket API                                 Boost.Asio
close()                            For TCP: ip::tcp::acceptor::close(), ip::tcp::socket::close()

                                   For UDP: ip::udp::socket::close()

                                   basic_socket::close()
connect()                          For TCP: ip::tcp::socket::connect()

                                   For UDP: ip::udp::socket::connect()

                                   basic_socket::connect()
getaddrinfo(), gethostbyaddr(),    For TCP: ip::tcp::resolver::resolve(),
gethostbyname(), getnameinfo(),    ip::tcp::resolver::async_resolve()
getservbyname(), getservbyport()
                                   For UDP: ip::udp::resolver::resolve(),
                                   ip::udp::resolver::async_resolve()

                                   ip::basic_resolver::resolve(),
                                   ip::basic_resolver::async_resolve()

gethostname()                      ip::host_name()
BSD Socket API                                Boost.Asio
getpeername()                           For TCP: ip::tcp::socket::remote_endpoint()

                                        For UDP: ip::udp::socket::remote_endpoint()

                                        basic_socket::remote_endpoint()
getsockname()                           For TCP: ip::tcp::acceptor::local_endpoint(),
                                        ip::tcp::socket::local_endpoint()

                                        For UDP: ip::udp::socket::local_endpoint()

                                        basic_socket::local_endpoint()

getsockopt()                            For TCP: ip::tcp::acceptor::get_option(),
                                        ip::tcp::socket::get_option()

                                        For UDP: ip::udp::socket::get_option()

                                        basic_socket::get_option()

inet_addr(), inet_aton(), inet_pton()   ip::address::from_string(),
                                        ip::address_v4::from_string(),
                                        ip_address_v6::from_string()
BSD Socket API                        Boost.Asio
inet_ntoa(), inet_ntop()        ip::address::to_string(), ip::address_v4::to_string(),
                                ip_address_v6::to_string()
ioctl()                         For TCP: ip::tcp::socket::io_control()

                                For UDP: ip::udp::socket::io_control()

                                basic_socket::io_control()
listen()                        For TCP: ip::tcp::acceptor::listen()

                                basic_socket_acceptor::listen()
poll(), select(), pselect()     io_service::run(), io_service::run_one(),
                                io_service::poll(), io_service::poll_one()

                                Note: in conjunction with asynchronous operations.
BSD Socket API                       Boost.Asio
readv(), recv(), read()        For TCP: ip::tcp::socket::read_some(),
                               ip::tcp::socket::async_read_some(),
                               ip::tcp::socket::receive(),
                               ip::tcp::socket::async_receive()

                               For UDP: ip::udp::socket::receive(),
                               ip::udp::socket::async_receive()

                               basic_stream_socket::read_some(),
                               basic_stream_socket::async_read_some(),
                               basic_stream_socket::receive(),
                               basic_stream_socket::async_receive(),
                               basic_datagram_socket::receive(),
                               basic_datagram_socket::async_receive()

recvfrom()                     For UDP: ip::udp::socket::receive_from(),
                               ip::udp::socket::async_receive_from()

                               basic_datagram_socket::receive_from(),
                               basic_datagram_socket::async_receive_from()
BSD Socket API                                     Boost.Asio
send(), write(), writev()   For TCP: ip::tcp::socket::write_some(),
                            ip::tcp::socket::async_write_some(), ip::tcp::socket::send(),
                            ip::tcp::socket::async_send()

                            For UDP: ip::udp::socket::send(), ip::udp::socket::async_send()

                            basic_stream_socket::write_some(),
                            basic_stream_socket::async_write_some(),
                            basic_stream_socket::send(), basic_stream_socket::async_send(),
                            basic_datagram_socket::send(),
                            basic_datagram_socket::async_send()

sendto()                    For UDP: ip::udp::socket::send_to(), ip::udp::socket::async_send_to()

                            basic_datagram_socket::send_to(),
                            basic_datagram_socket::async_send_to()
setsockopt()                For TCP: ip::tcp::acceptor::set_option(), ip::tcp::socket::set_option()

                            For UDP: ip::udp::socket::set_option()

                            basic_socket::set_option()
BSD Socket API                           Boost.Asio
shutdown()                      For TCP: ip::tcp::socket::shutdown()

                                For UDP: ip::udp::socket::shutdown()

                                basic_socket::shutdown()
sockatmark()                    For TCP: ip::tcp::socket::at_mark()

                                basic_socket::at_mark()
socket()                        For TCP: ip::tcp::acceptor::open(),
                                ip::tcp::socket::open()

                                For UDP: ip::udp::socket::open()

                                basic_socket::open()

socketpair()                    local::connect_pair()

                                Note: POSIX operating systems only.

[KGC 2012]Boost.asio를 이용한 네트웍 프로그래밍

  • 1.
    Boost.Asio를 이용한 네트웍 프로그래밍 최흥배 Twitter : @jacking75
  • 2.
    Visual C++ MVP2008 ~ 2011
  • 3.
  • 5.
  • 7.
  • 8.
    Boost.Asio란? • Boost 라이브러리의일부. 주로 네트웍 프로그래밍에 사용 • Asynchronous I/O (비동기 입출력). • I/O와 같이 시간이 걸리는 처리를 OS의 비동기 기능과 스레 드를 사용하여 처리. • 파일 입출력이나 시리얼 입출력, 또는 일반적인 비동기 프로 그래밍에서도 사용. • 멀티 플랫폼 지원.
  • 9.
    OS 플랫폼 별구현 • Linux Kernel 2.4 select를 사용하므로 FD_SIZE 크기를 넘지 못함 • Linux Kerner 2.6 epoll을 사용 • FreeBSD, Mac OS X Kqueue를 사용 • Solaris /dev/poll을 사용 • Windows(Windows 2000 이후) Overlapped I/O와 I/O Completion을 사용
  • 10.
    Boost.Asio의 주요 클래스 •boost::asio::io_service 가장 중요 • ip::tcp::socket (http에도 사용) • ip::udp::socket • ip::icmp::socket (ping 등에 사용) • ssl::context (Open SSL이 필요) • serial_port • boost::deadline_timer
  • 11.
    믿을 수 있나? •신뢰성이 높음. • (아마도)차기 C++ 표준에 들어갈 확률이 높음... • 한국의 몇몇 온라인 게임에서 이미 사용 중, 또 한국의 모 대형 IT 회사의 내부 네트웍 라이브러리로 Boost.Asio를 사용하고 있다고 함 .
  • 12.
  • 13.
  • 14.
  • 15.
    Boost 빌드? 혹은 직접 빌드 하기…..
  • 16.
  • 17.
    Demo : Asio를동기 모드로 사용 Tutotial_001
  • 18.
    int main() { asio::io_service io_service; tcp::socket socket(io_service); boost::system::error_code error; auto endpoint = tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 31400); socket.connect( endpoint, error ); if (error) { std::cout << "connect 실패 : " << error.message() << std::endl; } else { std::cout << "connected" << std::endl; } return 0; }
  • 19.
    Demo : Asio를비동기 모드로 사용 Tutotial_002
  • 20.
    void connect() { auto endpoint = tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 31400); socket_.async_connect( endpoint, boost::bind( &Client::on_connect, this, asio::placeholders::error) ); } void on_connect(const boost::system::error_code& error) { if (error) { std::cout << "connect failed : " << error.message() << std::endl; } else { std::cout << "connected" << std::endl; } }
  • 21.
    int main() { asio::io_service io_service; Client client(io_service); client.connect(); io_service.run(); }
  • 22.
    Boost application performance usingasynchronous I/O http://www.ibm.com/developerworks/linux/library/l-async/
  • 23.
    Typical flow ofthe synchronous blocking I/O model
  • 24.
    Typical flow ofthe synchronous non-blocking I/O model
  • 25.
    Typical flow ofthe asynchronous blocking I/O model (select)
  • 26.
    Typical flow ofthe asynchronous non-blocking I/O model
  • 27.
  • 28.
    Boost.Asio와 IOCP g_hIocp = CreateIoCompletionPort(INVALID_HANDLE_VA LUE, NULL, 0, 0); asio::io_service io_service; client = accept(server, (struct sockaddr*)&addr, &addrlen); tcp::socket socket(io_service); g_hIocp = io_service.run(); CreateIoCompletionPort((HANDLE)client, g_hIocp, (DWORD)con, 0); GetQueuedCompletionStatus(g_hIocp, &readbytes, &dwCompKey, (LPOVERLAPPED *)&pOverlap, INFINITE);
  • 29.
    Asio의 비동기 모델- 스레드 모델 애플리케이션 callback 함수 오브젝트 run() Boost 소켓 io_service int main() I/O { asio::io_service io_service; Client client(io_service); OS client.connect(); io_service.run(); } 참조 : http://d.hatena.ne.jp/Softgels/20090304/1236123151
  • 30.
    Asio의 비동기 모델- 멀티 스레드 모델 Boss 스레드 만들고, 종료까지 기다림 Worker 스레드 Worker 스레드 callback 함수 오브젝트 Worker 스레드 run() Boost 소켓 for( INT32 i = 0; i < 3; ++i ) io_service { I/O m_IoWorkThreadList.emplace_back ( std::thread( boost::bind( OS &boost::asio::io_service::run, &m_IOService) ) ); } 참조 : http://d.hatena.ne.jp/Softgels/20090304/1236123151
  • 31.
    io_service 클래스  소켓,타이머 등 비동기 입출력 이벤트를 디스패치하는 클래스  멤버 함수 size_t run(); size_t run( boost::system::error_code& e ); size_t run_one(); size_t run_one( boost::system::error_code& e ); size_t pool(); size_t pool( boost::system::error_code& e ); size_t pool_one(); size_t pool_one( boost::system::error_code& e );  run 함수는 모든 이벤트가 처리될 때까지 블럭된다.  run_one 함수는 하나의 이벤트가 처리될 때까지 블럭된다.  poll 함수는 블럭하지 않으면서 처리할 이벤트를 모두 처리하고 종료한다.  poll_one 함수는 블럭하지 않으면서 1개((또는 0개) 이벤트를 처리하고 종료한다.  모두 실행한 이벤트 개수를 반환한다.
  • 32.
     정지 함수 void stop(); void reset();  run이나 poll 함수의 처리 루프를 정지할 때 stop 함수를 사용한다.  stop 사용 후에 다시 run이나 poll을 사용하면 에러가 발생. 이때는 reset 함수를 사용해야 한다.
  • 33.
  • 34.
    TCP/UDP 통신  TCP/IP boost::asio::ip::tcp  UDP/IP boost::asio::ip::udp  protocol v4(), v6()  endpoint 접속 주소를 지정 INADDR_ANY로 포트번호만 지정하던가 IP와 포트번호를 지정할 수 있다.  resolver 호스트 이름에서 IP 주소로 변환시키는 클래스.  acceptor 클라이언트로부터 TCP 접속을 받아 들이기 위해서 서버에서 사용하는 클래스. accpet 처리에는 동기와 비동기 두 개의 버전이 있다.
  • 35.
    buffer 클래스  고정사이즈 버퍼 배열, boost::array, std::vector, std::string에 buffer 함수를 적용하며 기본 입출력에 넘길 수 있는 버퍼 오브젝트가 된다. size_t 인수로 사이즈를 지정하면 모두 바이트 단위로 지정한다.  함수 버퍼 const_buffer 또는 mutable_buffer를 저장한 컨테이너는 기본 입출력 함수에 넘길 수 있다. read 계열 함수에 넘긴 경우 모든 버퍼의 내용을 연결된 것으로 저장된다.  스트림 버퍼 STL의 스트림 버퍼로서 사용할 수 있으며 기본 입출력 버퍼이다.
  • 36.
    기본 입출력  동기읽기 완료 조건을 만족하던가 에러가 발생할 때까지 블럭. read_until에서는 구분 문자 delim을 읽을 때까지 블럭한다. ErrorHandler는 아래의 시그네쳐를 가진 함수 오브젝트를 지정할 수 있다. void error_han( const boost::system::error_code& e );  비동기 읽기 블럭킹 하지 않고 버퍼에 데이터를 읽어 들인다. 완료하던가 에러가 발생하면 소켓에 연결된 io_service의 run 함수에서 Handler 함수 오브젝트를 호출한다. Handler에는 아래의 시그네처를 가진 함수 오브젝트를 지정할 수 있다. void condition( const boost::system::error_code& e, size_t bytes_transferred );  동기 쓰기 버퍼로 부터 데이터를 쓴다. 완료 또는 에러가 발생할 때까지 블럭킹 한다.  비동기 쓰기 블럭킹 하지 않고 버퍼에서 데이터를 쓴다. 완료 또는 에러가 발생하면 소켓에 연결된 io_service의 run 함수로부터 Handler 함수 오브젝트 가 호출된다. Handler에는 아래의 시그네처를 가진 함수 오브젝트를 지정할 수 있다. void condition( const boost::system::error_code& e, size_t bytes_transferred );
  • 37.
    error_code 클래스  시스템의존 에러코드를 랩핑한 클래스.  bool 타입으로 평가하면 에러 때는 true, 정상일 때는 false.  message 및 wmessage 멤버 함수를 사용하면 상세한 에러를 문자열로 얻을 수 있다. class error_code { public: error_code(); error_code( value_type val, error_category cat ); operator unspecified_bool_type() const; bool operator() const; value_type value() const; error_category category() const; int to_error() const; std::string message() const; wstring_t wmessage() const; };
  • 38.
    Demo : 간단한채팅 Tutotial_Chat http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/examples.html#boost_asio.examples.allocation
  • 39.
    Demo : io_service를포함하는 설계 Tutorial_IOServicePattern
  • 40.
    Demo : 간단한백그라운드 처리 Tutorial_SimpleBackground
  • 41.
  • 42.
     run()은 이벤트큐가 없을 때까지 대기,  poll()은 준비가 끝난 이벤트를 실행.  준비가 끝난 이벤트란 핸들러를 실행할 수 있는 준비가 끝난 것을 말한다. 그러므로 타이머나 IO 완료 통지는 poll()로는 기다릴 수 없다.  poll()에서 대기하지 않은 핸들러는 poll()이 끝난 후에 run()이나 run_one() 등으로 처리하면 된다.
  • 43.
    Demo : 워커스레드 패턴 Tutorial_WorkerThreadPattern
  • 44.
    Demo : 이벤트디스패치 스레드 Tutorial_EventDispatch
  • 45.
    Io_service::work 비동기 처리를 등록하기전에 io_servive::run()을 호출하면 io 작업이 완료되 어 버리고 스레드는 종료된다. 이것을 방지하기 위해 io_service::work를 사용한다. Io_service::work를 사용하면 work가 파괴될 때까지 ioLservice::run()은 종료 하지 않는다 boost::shared_ptr<boost::asio::io_service::work> work_; work_.reset(new boost::asio::io_service::work(io_service_));
  • 46.
    strand 클래스 class strand { public: strand( io_service& io ); template< typename Handler > unspecified wrap( Handler han ); }; 핸들러 함수 han을 랩핑해서 동일의 strand로 랩핑된 핸들러가 복수의 스레드에서 병렬로 실행되 지 않도록 동기화된 핸들러 함수로 바꾸어준다. strandobj.wrap(han)의 결과는 그대로 기본 입출력 함수나 타이머 등의 핸들러 함수로서 사용할 수 있다. async_op_1(..., s.wrap(a)); async_op_2(..., s.wrap(b));
  • 47.
    Demo : Windows에서파일을 비동 기로 읽기 Tutorial_AsyncFileRead
  • 48.
    Demo : boost::asio::deadline_timer의 cancel() Tutorial_TimerCancel
  • 49.
    Demo : boost::asio::deadline_timer의 cancel_one() Tutorial_TimerCancelOne
  • 50.
    Demo : post와dispatch의 차이 Tutorial_post_dispatch
  • 51.
     dispatch()에서 호출한함수는 dispatch()를 호출한 곳과 같은 스 레드에서 호출된다. 그러나 post()의 경우는 다른 스레드에서 호출될 수 있다.  dispatch()는 호출한 곳의 함수가 비동기로 실행되고 있는 경우 비동기가 아닌 직접 함수를 호출하거나, post()와 같이 Queue에 등록하여 비동기로 실행한다.  dispatch()의 경우 핸들러 함수 콜스택이 깊어질 수 있으나(재귀 호출을 하면) post()는 호출한 핸들러 함수에서 제어 흐름이 일단 io_service 내의 메시지 루프로 돌아간 후 post 된 핸들러 함수가 호출되므로 콜 스택이 깊어지지 않는다.  dispatch()의 경우 컨텍스트가 바뀌지 않으므로 또 내용이 관련 된 핸들러가 연속해서 불려지므로 참조 국소성이 유지되지 않을 까 생각한다.
  • 54.
  • 55.
    참고  Boost 라이브러리공식 사이트 http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio.html  boostpro 사이트 http://www.boostpro.com/download/  Boost 라이브러리 직접 빌드하기 http://jacking.tistory.com/986  asio C++ Library http://think-async.com/Asio  Boost e-Book http://en.highscore.de/cpp/boost/
  • 56.
    resolver를 사용한 도메인네임 to IP 어 드레스 #include <boost/asio.hpp> #include <boost/bind.hpp> #include <iostream> namespace asio = boost::asio; namespace ip = asio::ip; class Client { asio::io_service& io_service_; ip::tcp::resolver resolver_; public: Client(asio::io_service& io_service) : io_service_(io_service), resolver_(io_service) { ip::tcp::resolver::query query("google.com", "http"); resolver_.async_resolve(query, boost::bind( &Client::on_resolve, this, asio::placeholders::error, asio::placeholders::iterator) ); }
  • 57.
    private: voidon_resolve(const boost::system::error_code& error, ip::tcp::resolver::iterator endpoint_iterator) { if (error) { std::cout << error.message() << std::endl; } else { ip::tcp::resolver::iterator end; for (; endpoint_iterator != end; ++endpoint_iterator) { std::cout << endpoint_iterator->endpoint().address().to_string() << std::endl; } } } }; int main() { asio::io_service io_service; Client client(io_service); io_service.run(); }
  • 58.
    자신의 Host 이름얻기 #include <boost/asio.hpp> #include <iostream> namespace asio = boost::asio; int main() { const std::string host_name = asio::ip::host_name(); std::cout << "hostname: " << host_name << std::endl; }
  • 59.
    비동기 핸들러 디버그지원 1.47 버전부터 지원하는 것으로 BOOST_ASIO_ENABLE_HANDLER_TRACKING 라는 비동기 핸들러 디 버그 지원 매크로를 사용하면 consol에 로그가 찍힌다. #define BOOST_ASIO_ENABLE_HANDLER_TRACKING @asio|1310697299.008735|0*1|socket@0012FE5C.async_send @asio|1310697299.008735|0*2|socket@0012FE5C.async_receive @asio|1310697299.008735|>1|ec=system:0,bytes_transferred=8 request : request @asio|1310697299.008735|<1| @asio|1310697299.039985|>2|ec=system:0,bytes_transferred=9 response : response @asio|1310697299.039985|<2| @asio|1310697300.039985|0*3|socket@0012FE5C.async_send @asio|1310697300.039985|0*4|socket@0012FE5C.async_receive @asio|1310697300.039985|>3|ec=system:0,bytes_transferred=8 request : request @asio|1310697300.039985|<3| @asio|1310697300.039985|>4|ec=system:0,bytes_transferred=9 response : response @asio|1310697300.039985|<4| @asio|1310697301.039985|0*5|socket@0012FE5C.async_send @asio|1310697301.039985|0*6|socket@0012FE5C.async_receive @asio|1310697301.039985|>5|ec=system:0,bytes_transferred=8 request : request ...
  • 60.
    출력 양식 <tag>|<timestamp>|<action>|<description> <tag>는 보통@asio로 된다. 이 행은 트랙킹 메시지라는 것을 알려준다. <timestamp>는 유닉스 시간이다. 1970년 1월1일부터의 시간. 초와 밀리 초 가 출력된다. <action> 규칙은 아래와 같음 >n : 프로그램 핸들러 수 <n : 종료한 핸들러 수 !n : 예외로 종료한 핸들러 수 ~n : 호출된 적이 없이 파괴된 핸들러 수 n*m : 새롭게 작성한 비동기 조작 수 n과 완료 대기 핸들러 수 m n : 그 외 조작을 한 핸들러 수. close나 cancel 등. <description>는 동기 or 비동기 조작이 실행되었는가를 출력한다. 형식은 「<object-type>@<pointer>.<operation>」. 핸들러 인수는 콤마로 구 분된 리스트로서 출력한다.
  • 61.
    완료 조건 transfer_all_t transfer_all(); transfer_at_least_ttransfer_at_least( size_t min ); 기본 입출력 함수의 CompletionCondition 인수로 넘길 수 있는 오브젝트 예를들면 transfer_all() 함수를 넘기면 버퍼 모두를 사용할 때까지 입출력이 진행된다. transfer_at_least(m)를 넘기면 최저 m 바이트 이상의 쓰기가 진행될 때까지는 입출력이 진행된다. 보통 아래와 같은 시그네쳐를 가진 함수 오브젝트를 넘길 때 사용한다. true를 넘기면 입출력 완료 로서 좋다는 것을 표시한다. bool condition( const boost::system::error_code& e, size_t bytes_transferred );
  • 62.
    boost::array<char, 128> buf; boost::system::error_codeec; std::size_t n = boost::asio::read( sock, boost::asio::buffer(buf), boost::asio::transfer_all(), ec ); if (ec) { // An error occurred. } else { // n == 128 } boost::array<char, 128> buf; boost::system::error_code ec; std::size_t n = boost::asio::read( sock, boost::asio::buffer(buf), boost::asio::transfer_at_least(64), ec ); if (ec) { // An error occurred. } else { // n >= 64 && n <= 128 } boost::asio::async_read( sock, boost::asio::buffer(data, size), boost::asio::transfer_at_least(32), handler );
  • 63.
    BSD Socket -Boost.Asio BSD Socket API Boost.Asio socket descriptor - int (POSIX) or SOCKET For TCP: ip::tcp::socket, ip::tcp::acceptor (Windows) For UDP: ip::udp::socket basic_socket, basic_stream_socket, basic_datagram_socket, basic_raw_socket in_addr, in6_addr ip::address, ip::address_v4, ip::address_v6 sockaddr_in, sockaddr_in6 For TCP: ip::tcp::endpoint For UDP: ip::udp::endpoint ip::basic_endpoint accept() For TCP: ip::tcp::acceptor::accept() basic_socket_acceptor::accept() bind() For TCP: ip::tcp::acceptor::bind(), ip::tcp::socket::bind() For UDP: ip::udp::socket::bind() basic_socket::bind()
  • 64.
    BSD Socket API Boost.Asio close() For TCP: ip::tcp::acceptor::close(), ip::tcp::socket::close() For UDP: ip::udp::socket::close() basic_socket::close() connect() For TCP: ip::tcp::socket::connect() For UDP: ip::udp::socket::connect() basic_socket::connect() getaddrinfo(), gethostbyaddr(), For TCP: ip::tcp::resolver::resolve(), gethostbyname(), getnameinfo(), ip::tcp::resolver::async_resolve() getservbyname(), getservbyport() For UDP: ip::udp::resolver::resolve(), ip::udp::resolver::async_resolve() ip::basic_resolver::resolve(), ip::basic_resolver::async_resolve() gethostname() ip::host_name()
  • 65.
    BSD Socket API Boost.Asio getpeername() For TCP: ip::tcp::socket::remote_endpoint() For UDP: ip::udp::socket::remote_endpoint() basic_socket::remote_endpoint() getsockname() For TCP: ip::tcp::acceptor::local_endpoint(), ip::tcp::socket::local_endpoint() For UDP: ip::udp::socket::local_endpoint() basic_socket::local_endpoint() getsockopt() For TCP: ip::tcp::acceptor::get_option(), ip::tcp::socket::get_option() For UDP: ip::udp::socket::get_option() basic_socket::get_option() inet_addr(), inet_aton(), inet_pton() ip::address::from_string(), ip::address_v4::from_string(), ip_address_v6::from_string()
  • 66.
    BSD Socket API Boost.Asio inet_ntoa(), inet_ntop() ip::address::to_string(), ip::address_v4::to_string(), ip_address_v6::to_string() ioctl() For TCP: ip::tcp::socket::io_control() For UDP: ip::udp::socket::io_control() basic_socket::io_control() listen() For TCP: ip::tcp::acceptor::listen() basic_socket_acceptor::listen() poll(), select(), pselect() io_service::run(), io_service::run_one(), io_service::poll(), io_service::poll_one() Note: in conjunction with asynchronous operations.
  • 67.
    BSD Socket API Boost.Asio readv(), recv(), read() For TCP: ip::tcp::socket::read_some(), ip::tcp::socket::async_read_some(), ip::tcp::socket::receive(), ip::tcp::socket::async_receive() For UDP: ip::udp::socket::receive(), ip::udp::socket::async_receive() basic_stream_socket::read_some(), basic_stream_socket::async_read_some(), basic_stream_socket::receive(), basic_stream_socket::async_receive(), basic_datagram_socket::receive(), basic_datagram_socket::async_receive() recvfrom() For UDP: ip::udp::socket::receive_from(), ip::udp::socket::async_receive_from() basic_datagram_socket::receive_from(), basic_datagram_socket::async_receive_from()
  • 68.
    BSD Socket API Boost.Asio send(), write(), writev() For TCP: ip::tcp::socket::write_some(), ip::tcp::socket::async_write_some(), ip::tcp::socket::send(), ip::tcp::socket::async_send() For UDP: ip::udp::socket::send(), ip::udp::socket::async_send() basic_stream_socket::write_some(), basic_stream_socket::async_write_some(), basic_stream_socket::send(), basic_stream_socket::async_send(), basic_datagram_socket::send(), basic_datagram_socket::async_send() sendto() For UDP: ip::udp::socket::send_to(), ip::udp::socket::async_send_to() basic_datagram_socket::send_to(), basic_datagram_socket::async_send_to() setsockopt() For TCP: ip::tcp::acceptor::set_option(), ip::tcp::socket::set_option() For UDP: ip::udp::socket::set_option() basic_socket::set_option()
  • 69.
    BSD Socket API Boost.Asio shutdown() For TCP: ip::tcp::socket::shutdown() For UDP: ip::udp::socket::shutdown() basic_socket::shutdown() sockatmark() For TCP: ip::tcp::socket::at_mark() basic_socket::at_mark() socket() For TCP: ip::tcp::acceptor::open(), ip::tcp::socket::open() For UDP: ip::udp::socket::open() basic_socket::open() socketpair() local::connect_pair() Note: POSIX operating systems only.