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를 사용하고 있다고 함 .
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 함수를 사용해야 한다.
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
42. run()은 이벤트 큐가 없을 때까지 대기,
poll()은 준비가 끝난 이벤트를 실행.
준비가 끝난 이벤트란 핸들러를 실행할 수 있는 준비가 끝난 것을 말한다.
그러므로 타이머나 IO 완료 통지는 poll()로는 기다릴 수 없다.
poll()에서 대기하지 않은 핸들러는 poll()이 끝난 후에 run()이나 run_one()
등으로 처리하면 된다.
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));
51. dispatch()에서 호출한 함수는 dispatch()를 호출한 곳과 같은 스
레드에서 호출된다.
그러나 post()의 경우는 다른 스레드에서 호출될 수 있다.
dispatch()는 호출한 곳의 함수가 비동기로 실행되고 있는 경우
비동기가 아닌 직접 함수를 호출하거나, post()와 같이 Queue에
등록하여 비동기로 실행한다.
dispatch()의 경우 핸들러 함수 콜스택이 깊어질 수 있으나(재귀
호출을 하면) post()는 호출한 핸들러 함수에서 제어 흐름이 일단
io_service 내의 메시지 루프로 돌아간 후 post 된 핸들러 함수가
호출되므로 콜 스택이 깊어지지 않는다.
dispatch()의 경우 컨텍스트가 바뀌지 않으므로 또 내용이 관련
된 핸들러가 연속해서 불려지므로 참조 국소성이 유지되지 않을
까 생각한다.
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)
);
}
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_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 );
62. 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 );
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.