CafeM0ca

[C++]boost asio 튜토리얼 정리 본문

Programming/C++

[C++]boost asio 튜토리얼 정리

M0ca 2018. 4. 5. 07:02
반응형

타이머 동기화 사용하기

asio에서 사용하는 모든 프로그램은 최소 하나의 boost::asio::io_context 객체가 필요하다. 이 클래스는 입출력 기능에 접근할 수 있다.

boost::asio::deadline_timer는 입출력 기능을 제공하며 항상 io_context를 첫번째 인자로 참조한다. 두번째 인자는 선언으로부터 설정한 n초 후에 만료된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 //timer.cpp
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
 
int main()
{
  boost::asio::io_context io;
 
  boost::asio::deadline_timer t(io, boost::posix_time::seconds(5));
  t.wait();
 
  std::cout << "Hello, world!" << std::endl;
 
  return 0;
}
cs



타이머 비동기화 사용하기

asio 비동기 기능성은 비동기 준비가 완료되면 콜백함수가 호출된다. 이 프로그램에서 print는 비동기 대기가 끝나면 호출된다.

deadline_timer::async_wait() 함수를 호출하여 비동기 대기를 한다. 이 함수를 호출할때 print로 정의된 콜백 핸들러를 전달한다.

io_context::run() 맴버함수를 반드시 io_context 객체에 실행한다.

asio 라이브러리는 제공한다 콜백 핸들러가 오직 io_context::run() 함수를 호출하는 쓰레드에서만 호출된다는 보증을. 

io_context::run() 함수를 호출하지 않으면 비동기 대기 완료를 위한 콜백이 호출되지 않는다.

중요한것은 io_context::run() 함수 호출전에 무언가 일을 줘야한다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
 
void print(const boost::system::error_code&)
{
    std::cout << "Hello, world!" << std::endl;
 
}
 
int main()
{
    boost::asio::io_context io;
    boost::asio::deadline_timer t(io,boost::posix_time::seconds(5));
    t.async_wait(&print);
    io.run();
    return 0;
}
 
cs


핸들러에 인자 바인딩하기

asio를 사용하여 타이머를 반복하도록 구현하려면 콜백함수에 만료시간을 바꿔주면 된다. 그러면 새롭게 비동기 대기를 시작한다.

pirnt함수에 2개의 매개변수를 추가한다. 

- 타이머 객체를 가리키는 포인터

- 타이머를 6번 쓰면 멈추게하는 카운터

이렇게 추가함으로써 타이머에 시간 조절이 가능해진다. 1초씩 타이머를 줄이면서

 이 프로그램은 카운터를 사용하는데 타이머가 6번째에 멈추기하기 위해서. 그러나 io_context에게 멈출것인지 명시적으로 묻지않는 것을 봐야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
 
void print(const boost::system::error_code&,
        boost::asio::deadline_timer *t,int *count)
{
    if(*count < 5)
    {
        std::cout << *count << std::endl;
        ++(*count);
        t->expires_at(t->expires_at() + boost::posix_time::seconds(1));
        t->async_wait(boost::bind(print,boost::asio::placeholders::error,t,count));
    }
}
 
int main()
{
    boost::asio::io_context io;
    int count = 0;
    boost::asio::deadline_timer t(io,boost::posix_time::seconds(1));
    t.async_wait(boost::bind(print,boost::asio::placeholders::error,&t,&count));
    io.run();
    std::cout << "Final count is " << count << std::endl;
    return 0;
}
 
cs


멀티쓰레드 타이머

컴파일 옵션 : g++ -I(대문자 i) /path/path/boost_1_66_0 tiemr.cpp -o timer -l boost_system -l boost_thread -pthread


동기화 준비가 되면 각 콜백 핸들러는 boost::asio::io_service::strand 객체에게 "bound" 상태를 전달한다. strand객체에게 핸들러를 bind함으로써 동시에 실행되는것을 막는다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
 
class printer
{
public:
  printer(boost::asio::io_context& io)
    : strand_(io),
      timer1_(io, boost::posix_time::seconds(1)),
      timer2_(io, boost::posix_time::seconds(1)),
      count_(0)
  {
    timer2_.async_wait(boost::asio::bind_executor(strand_,
          boost::bind(&printer::print2, this)));
 
    timer1_.async_wait(boost::asio::bind_executor(strand_,
          boost::bind(&printer::print1, this)));
 
  }
 
  ~printer()
  {
    std::cout << "Final count is " << count_ << std::endl;
  }
 
  void print1()
  {
    if (count_ < 10)
    {
      std::cout << "Timer 1: " << count_ << std::endl;
      ++count_;
 
      timer1_.expires_at(timer1_.expires_at() + boost::posix_time::seconds(1));
 
      timer1_.async_wait(boost::asio::bind_executor(strand_,
            boost::bind(&printer::print1, this)));
    }
  }
 
  void print2()
  {
    if (count_ < 10)
    {
      std::cout << "Timer 2: " << count_ << std::endl;
      ++count_;
 
      timer2_.expires_at(timer2_.expires_at() + boost::posix_time::seconds(1));
 
      timer2_.async_wait(boost::asio::bind_executor(strand_,
            boost::bind(&printer::print2, this)));
    }
  }
 
private:
  boost::asio::io_context::strand strand_;
  boost::asio::deadline_timer timer1_;
  boost::asio::deadline_timer timer2_;
  int count_;
};
 
int main()
{
  boost::asio::io_context io;
  printer p(io);
  boost::thread t(boost::bind(&boost::asio::io_context::run, &io));
  io.run();
  t.join();
 
  return 0;
}
 
cs


모든 소스코드는 boost tutorial의 소스코드다. https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/tutorial.html



2018/08/09 추가


daytime_client.cpp

이 클라이언트 프로그램은 아래의 동기화 서버 프로그램과 같이 돌아간다.

asio로 작동하는 프로그램에서는 io_context 객체가 최소한 하나가 필요하다. (io_service는 오래된 클래스다.)

ip::tcp::resolver

resolver는 쿼리 객체를 갖고 endpoints 리스트의 끝으로 바꾼다.  여기서는 argv[1]에 지정된 서버의 이름을 사용하여 쿼리를 만든다.


ip::tcp::resolver::iterator

resolver의 endpoints 리스트를 반환하기 위해 사용한다.


boost::asio::connect

위 과정에서 얻은것들은 IPv4와 IPv6 둘 다 될 수 있다. 작동하는것을 찾을 때 각각 해줘야한다. => 클라이언트의 특정 IP버전과 독립적으로 유지됨

boost::asio::connect는 이 과정을 자동으로 해준다.


ip::tcp::socket::read_some()

서버가 연결을 종료하면 ip::tcp::socket::read_some()은 boost::asio::error::eof()오류를 통해 종료한다.



daytime_server.cpp

asio를 사용해 동기화 서버 어플리케이션을 구현해보자.

make_daytime_string()을 통해 클라이언트에게 되보낼거다.  이 함수는 서버의 daytime을 재사용한다.


ip::tcp::acceptor 객체는 새로운 연결을 위해서 생성한다. TCP 13번포트로 ipv4버전을 응답받는다.

tcp::acceptor accpetor(io_context, tcp:;endpoint(tcp::v4(), 13));



위 서버 프로그램을 비동기로 작성해보자

클라이언트로부터 오는 요청을 서버의 io_context 객체가 사용하는데 io_context객체는 입출력 서비스와 소켓같은것을 제공한다.

반응형
Comments