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

25,188 views

Published on

KGC 2012 강연. Boost 라이브러리 중 Asio를 사용한 네트웍 프로그래밍 및 비동기 프로그래밍 소개

Published in: Technology

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

  1. 1. Boost.Asio를 이용한 네트웍 프로그래밍 최흥배 Twitter : @jacking75
  2. 2. Visual C++ MVP 2008 ~ 2011
  3. 3. http://devwith.com/
  4. 4. http://jacking.tistory.com/https://twitter.com/jacking75
  5. 5. Boost.Asio ?
  6. 6. Boost.Asio란?• Boost 라이브러리의 일부. 주로 네트웍 프로그래밍에 사용• Asynchronous I/O (비동기 입출력).• I/O와 같이 시간이 걸리는 처리를 OS의 비동기 기능과 스레 드를 사용하여 처리.• 파일 입출력이나 시리얼 입출력, 또는 일반적인 비동기 프로 그래밍에서도 사용.• 멀티 플랫폼 지원.
  7. 7. 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을 사용
  8. 8. 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
  9. 9. 믿을 수 있나?• 신뢰성이 높음.• (아마도)차기 C++ 표준에 들어갈 확률이 높음...• 한국의 몇몇 온라인 게임에서 이미 사용 중, 또 한국의 모 대형 IT 회사의 내부 네트웍 라이브러리로 Boost.Asio를 사용하고 있다고 함 .
  10. 10. 성능?
  11. 11. 편의성?
  12. 12. 직접 만들기….
  13. 13. Boost 빌드? 혹은 직접 빌드 하기…..
  14. 14. Demo !!!
  15. 15. Demo : Asio를 동기 모드로 사용Tutotial_001
  16. 16. 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;}
  17. 17. Demo : Asio를 비동기 모드로 사용Tutotial_002
  18. 18. 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; }}
  19. 19. int main(){ asio::io_service io_service; Client client(io_service); client.connect(); io_service.run();}
  20. 20. Boost application performanceusing asynchronous I/Ohttp://www.ibm.com/developerworks/linux/library/l-async/
  21. 21. Typical flow of the synchronous blocking I/O model
  22. 22. Typical flow of the synchronous non-blocking I/O model
  23. 23. Typical flow of the asynchronous blocking I/O model (select)
  24. 24. Typical flow of the asynchronous non-blocking I/O model
  25. 25. Proactor and Boost.Asio
  26. 26. 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);
  27. 27. 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
  28. 28. 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
  29. 29. 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개) 이벤트를 처리하고 종료한다. 모두 실행한 이벤트 개수를 반환한다.
  30. 30.  정지 함수 void stop(); void reset(); run이나 poll 함수의 처리 루프를 정지할 때 stop 함수를 사용한다. stop 사용 후에 다시 run이나 poll을 사용하면 에러가 발생. 이때는 reset 함수를 사용해야 한다.
  31. 31. io_service는 Asio의 시작이자 끝!!!
  32. 32. 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 처리에는 동기와 비동기 두 개의 버전이 있다.
  33. 33. buffer 클래스 고정 사이즈 버퍼 배열, boost::array, std::vector, std::string에 buffer 함수를 적용하며 기본 입출력에 넘길 수 있는 버퍼 오브젝트가 된다. size_t 인수로 사이즈를 지정하면 모두 바이트 단위로 지정한다. 함수 버퍼 const_buffer 또는 mutable_buffer를 저장한 컨테이너는 기본 입출력 함수에 넘길 수 있다. read 계열 함수에 넘긴 경우 모든 버퍼의 내용을 연결된 것으로 저장된다. 스트림 버퍼 STL의 스트림 버퍼로서 사용할 수 있으며 기본 입출력 버퍼이다.
  34. 34. 기본 입출력 동기 읽기 완료 조건을 만족하던가 에러가 발생할 때까지 블럭. 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 );
  35. 35. 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; };
  36. 36. Demo : 간단한 채팅Tutotial_Chat http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/examples.html#boost_asio.examples.allocation
  37. 37. Demo : io_service를 포함하는 설계Tutorial_IOServicePattern
  38. 38. Demo : 간단한 백그라운드 처리Tutorial_SimpleBackground
  39. 39. Demo : pollTutorial_poll
  40. 40.  run()은 이벤트 큐가 없을 때까지 대기, poll()은 준비가 끝난 이벤트를 실행. 준비가 끝난 이벤트란 핸들러를 실행할 수 있는 준비가 끝난 것을 말한다. 그러므로 타이머나 IO 완료 통지는 poll()로는 기다릴 수 없다. poll()에서 대기하지 않은 핸들러는 poll()이 끝난 후에 run()이나 run_one() 등으로 처리하면 된다.
  41. 41. Demo : 워커 스레드 패턴Tutorial_WorkerThreadPattern
  42. 42. Demo : 이벤트 디스패치 스레드Tutorial_EventDispatch
  43. 43. 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_));
  44. 44. 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));
  45. 45. Demo : Windows에서 파일을 비동 기로 읽기Tutorial_AsyncFileRead
  46. 46. Demo : boost::asio::deadline_timer의 cancel()Tutorial_TimerCancel
  47. 47. Demo : boost::asio::deadline_timer의 cancel_one()Tutorial_TimerCancelOne
  48. 48. Demo : post와 dispatch의 차이Tutorial_post_dispatch
  49. 49.  dispatch()에서 호출한 함수는 dispatch()를 호출한 곳과 같은 스 레드에서 호출된다. 그러나 post()의 경우는 다른 스레드에서 호출될 수 있다. dispatch()는 호출한 곳의 함수가 비동기로 실행되고 있는 경우 비동기가 아닌 직접 함수를 호출하거나, post()와 같이 Queue에 등록하여 비동기로 실행한다. dispatch()의 경우 핸들러 함수 콜스택이 깊어질 수 있으나(재귀 호출을 하면) post()는 호출한 핸들러 함수에서 제어 흐름이 일단 io_service 내의 메시지 루프로 돌아간 후 post 된 핸들러 함수가 호출되므로 콜 스택이 깊어지지 않는다. dispatch()의 경우 컨텍스트가 바뀌지 않으므로 또 내용이 관련 된 핸들러가 연속해서 불려지므로 참조 국소성이 유지되지 않을 까 생각한다.
  50. 50. 참고 자료
  51. 51. 참고 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/
  52. 52. 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) ); }
  53. 53. 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();}
  54. 54. 자신의 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; }
  55. 55. 비동기 핸들러 디버그 지원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=8request : request@asio|1310697299.008735|<1|@asio|1310697299.039985|>2|ec=system:0,bytes_transferred=9response : 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=8request : request@asio|1310697300.039985|<3|@asio|1310697300.039985|>4|ec=system:0,bytes_transferred=9response : 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=8request : request...
  56. 56. 출력 양식<tag>|<timestamp>|<action>|<description><tag>는 보통 @asio로 된다. 이 행은 트랙킹 메시지라는 것을 알려준다.<timestamp>는 유닉스 시간이다. 1970년 1월1일부터의 시간. 초와 밀리 초가 출력된다.<action> 규칙은 아래와 같음>n : 프로그램 핸들러 수<n : 종료한 핸들러 수!n : 예외로 종료한 핸들러 수~n : 호출된 적이 없이 파괴된 핸들러 수n*m : 새롭게 작성한 비동기 조작 수 n과 완료 대기 핸들러 수 mn : 그 외 조작을 한 핸들러 수. close나 cancel 등.<description>는 동기 or 비동기 조작이 실행되었는가를 출력한다.형식은 「<object-type>@<pointer>.<operation>」. 핸들러 인수는 콤마로 구분된 리스트로서 출력한다.
  57. 57. 완료 조건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 );
  58. 58. 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 );
  59. 59. 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()
  60. 60. BSD Socket API Boost.Asioclose() 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()
  61. 61. BSD Socket API Boost.Asiogetpeername() 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()
  62. 62. BSD Socket API Boost.Asioinet_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.
  63. 63. BSD Socket API Boost.Asioreadv(), 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()
  64. 64. BSD Socket API Boost.Asiosend(), 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()
  65. 65. BSD Socket API Boost.Asioshutdown() 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.

×