Boost 비동기 통신 서버&클라이언트를 정리중...
필수 개념인 Asio에 대해서 먼저 정리해야겠다 생각들어서
따로 게시글로 뺐다 ㅈㄱㄴ
boost 홈페이지에 설명이 잘 되어있어서, 요걸 정리하려고 한다
(첨부 링크 참조)
1. Boost.Asio ( Asynchronous I/O ) 가 뭐지?
Boost 공식홈피: Boost.Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach.
cf) 어떤 운영체제여도, 실행 가능하게 동작하게 할 수 있는 플랫폼이 크로스 플랫폼
먼저, Asio은 Async I/O의 줄임말이고 뜻대로 해석하면 비동기 I/O이다.
우리가 Asio 방식으로 소켓 통신을 구현할 때,
리눅스에서는 EPOLL, 윈도우에서는 IOCP라는 것을 사용할텐데
소켓 통신은 커널과 어플리케이션 사이만 고려해도 구현할게 참 많다
소켓 통신 관리도 해주고, 운영체제 구분 없이 사용 가능한 라이브러리가
boost에서 만든 Boost.Asio 라이브러리이다
2. Sync I/O 과 Async I/O
Boost에서 Asio가 어떻게 동작하는지 알아보기 이전에,
I/O 방식에 대해서 먼저 정리하려고 한다
I/O 방식은 동기식(Sync)과 비동기식(Async) 두 가지가 있다
Blocking과 Non-blocking 비슷한 개념이라고 생각하면 된다 (물론 다름!)
동기식이냐... 블록킹이냐... 비동기냐.... 논블록킹이냐...
이거도 알아야할게 많아서, 글의 마지막에 살짝 정리만 해두겠다 ↓
우선 Sync Blocking I/O와 Async Non-Blocking I/O에 대해서만 다루겠다
(1) Sync I/O (Blocking일 때)
I/O를 할 때, 어플에서 Read하고 Write를 호출해서 데이터를 읽고 쓴다
Sync I/O는 단순히 어플에서 Read()를 호출하여 I/O 기능을 수행하고,
커널이 다 읽어서 줄 때까지 기다린다
커널은 다 읽으면 어플한테 읽은 Data와 Read() 함수의 결과 값을 준다
Sync I/O는 이해하기 쉽고, 단순히 커널의 응답을 기다리기만 하면 돼서,
처리 방법도 간단하다는 장점이 있다
그러나, 다 읽었다는 응답이 오기전까지 어플은 blocking 되고,
다른건 할 수 없으셈..
EPOLL을 사용해서, socket FD를 등록해서 Non-blocking으로 구현할 수도 있음
하지만 boost.Asio로 구현할거니까 non-blocking은 생략하겠음
(2) Async I/O (Non-Blocking일 때)
I/O를 할 때, 어플에서 Read하고 Write를 호출해서 데이터를 읽고 쓰는건
Sync일 때와 Async일 때 모두 같은 기능을 수행한다
Async일 때 다른 점은 그러면 무엇이냐..
어플은 Read()를 호출해서 커널한테 읽어주셈 하면
커널은 ㅇㅋ 읽고 알려주겠음 하고 쿨하게 읽으러 간다
Read() 호출을 하고, 읽은 결과와는 상관 없이 결과를 return 받는다
그러면 어플은 커널이 다 읽었다는 결과를 알려주기 전까지
다른 작업을 하고 있을 수 있다
커널은 다 읽으면, 읽은 Data나 결과 값을 어플에게 알려주는데,
이 때는 다 읽었다! 라는 signal만 줄 수도 있고, callback 형태로 알려줄 수 있다
어플은 커널이 읽었다고 알려줄 때까지 다른 일 하다가
읽었다고 하면, 뭐 읽었나 보면 되는 것이다
아주 효율적이군.
3. Boost.Asio 사용 방법
Boost.Asio는 소켓이랑 비슷한 역할을 하는 I/O Object라는 것으로
Sync와 Async I/O를 둘다 구현이 가능하다
I/O Object는 다음과 같이 객체선언으로 사용하면 된다
#include <boost/asio.hpp>
boost::asio::io_context io_context;
(io_service 사용이 io_context로 바뀌었다고 한다;;;)
프로그램에는 io_context 객체 같은 I/O 기능을 실행할 수 있는 무언가가 있게 된다
요걸 이용해서, socket 통신도 하고~ 에러 발생하면 에러도 처리하고~
Boost Asio가 원래 우리가 커널을 통해 I/O 작업을 하던걸 중간에서 대신 해주는거라고
생각하면 이해가 쉽게 갈 것이다
soket FD를 사용해서 Read() Write() 하듯이
io_context를 사용해서 Read() Write() 하는거다
그러면 boost에서 io_context를 어떻게 사용해야하는지
Sync와 Async 구현을 나눠서 보도록 하겠다
(io_service랑 io_context가 같은거임)
(1) Boost.Asio - Sync
TCP 클라이언트일 때를 예로 들어보자
위에 그림은 Boost 홈페이지에 있는건데, 원래 설명에 조금 덧붙여서 정리하겠다
우리 프로그램에서 io_context 객체를 최소 한개는 갖고 있지 않은가?
그러면 요 io_context를 통해 TCP socket을 등록할 수 있다
boost::asio::io_context io_context;
boost::asio::ip::tcp::socket socket(io_context);
바로 요렇겜. 그러면 socket과 io_context가 등록(?) 됐다고 할 수 있겠다
이렇게 연결된 소켓으로 I/O operation을 수행할 수 있으시겠다.
이후 동작은 그림에 있는 번호와 함께 정리하겠다
1. 클라이언트라면 서버에 연결을 해야하지 않는가ㅏㅏ
tcp::socket socket(io_context);
boost::asio::connect(socket, endpoint);
endpoint는 뭐지? 라고 생각이 들텐데,
당연히 connect를 하려면, 연결할 서버의 IP 주소와 Port 번호를 알아야한다
tcp::resolver resolver(io_context);
tcp::resolver::query query("localhost", "7777");
tcp::resolver::iterator endpoint = resolver.resolve(query);
endpoint는 resolver라는 것을 통해서 할 수 있다
resolver는 연결하고자 하는 서버의 주소에 대해 DNS query를 해준다
즉, 어플에서 DNS 이름을 확인해주는 인터페이스를 제공해준다 ㅋ
(실제 연결해줄 수 있는 주소인지 아닌지)
2. io_context는 I/O Execution context에 전달하는 역할
어플과 커널 사이에 중간 다리 역할을 해주는 것임
3. io_context는 수행하고자하는 I/O 작업을 운영체제한테 알려줌
4. 운영체제는 I/O 작업을 실행한 결과를 io_context에게 return 해줌
5. return 결과를 받은 io_context는 어플한테 결과를 알려줘야 함
어떻게? boost::system::error_code를 통해서
6. 결과는 받는건 두 가지 방법으로 받음
1) error_code를 통해서 받기
boost::system::error_code ec;
socket.connect(server_endpoint, ec);
파라미터로 넣어서 받아오면 됨 ㅋ
2) I/O object가 throws 해주는 exception으로 받아와도 됨ㅋ
(2) Boost.Asio - Async
그러면 비동기일 때는 어떻게 달라질까를 보겠다
1. connect를 하는건 똑같은데, 함수의 이름만 달라진다
socket.async_connect(server_endpoint, your_completion_handler);
connect() 대신 async_connect() 함수를 사용하고,
비동기 통신이기 때문에, 두번째 파라미터에 callback 함수만 등록해주면
실행결과에 대해 callback을 통해 받을 수 있다
2. I/O 객체를 통해 I/O 실행 context에게 요청을 전달한다 ~_~
3. I/O 실행 context는 어플이 요청한 Asynchronous connect에 대해
운영체제에게 signal을 보낸다
↑ 여기까지의 과정은 사실 동기식 방식과 비슷한데,
다른건 시간이다.
운영체제에 I/O 작업이 전달되는 동안의 시간은 비동기식에서는 포함되지 않는다
4. OS는 I/O 작업에 대해 완료가 되면, io_context에게 알려줌
5. 어플에서는 비동기 작업에 대해 시작한다는 의미로 io_context::run()를 호출해야 함
6. io_context는 실행 결과에 대해 callback 함수에 전달한다
↑ 위에 그림에서 화살표와 그냥 점선이 있는데
비동기식이니까 굳이 순서가 아닌 단계(?)로만 생각하자
실질적인 코드는 다음 글에서.....
cf) Sync & Async & Blocking & Non-Blocking 차이점
요건 오늘 주제를 정리하면서 알게된건데, 첨부링크에 있는 자바지기 님의 글을 참고해서 정리했따
system call을 이해하기 쉽게 함수 호출이라 칭하겠다
1. Sync & Async 차이는? → 누가 함수 완료 시키는지가 중요
- Sync: 어플에서 호출한 함수의 완료를 기다림 (ex. return 결과)
- Async: 커널에서 어플이 호출한 함수를 완료시키고, 어플은 안 기다리고 다른거 함
- (ex. callback 등록 후 다른거 함)
2. Blocking & Non-Blocking 차이는? → 언제 return하는지가 중요
- Blocking: 어플이 함수를 호출 → 운영체제 대기 큐에 등록 → 함수 실행 완료 후 결과를 return 받음
- Non-Blocking: 어플이 함수를 호출 → 실행완료 되든 안되든 결과를 바로 return 받음
3. Sync & Blocking 차이는?
- Sync: 어플에서 함수 return 완료를 기다리는 동안 대기 큐에 머물지 않아도 됨 (필수X)
- O_NONBLOCK 옵션 등을 사용해서 Non-blocking으로 바로 return 받을 수 있음
- Blocking: 어플에서 함수 return 완료를 기다리는 동안 대기 큐에 머물러야 함 (필수 O)
- 완료되어서 return 받을 때까지 킵고잉~
4. Async & Non-Blocking 차이는?
- Async: 함수 실행이 잘 됐다!에 대한 결과가 아니라 함수 실행 했다!에 대한 결과를 바로 return
- Non-Blocking: 함수 실행이 잘 됐다! 데이터가 없어서 읽을 수는 없다!에 대한 결과를 바로 return
* Boost Asio 설명
www.boost.org/doc/libs/1_76_0/doc/html/boost_asio/overview/core/basics.html
Basic Boost.Asio Anatomy - 1.76.0
Boost.Asio may be used to perform both synchronous and asynchronous operations on I/O objects such as sockets. Before using Boost.Asio it may be useful to get a conceptual picture of the various parts of Boost.Asio, your program, and how they work together
www.boost.org
* Sync&Async I/O 설명
developer.ibm.com/technologies/linux/articles/l-async/
Boost application performance using asynchronous I/O
The most common input/output (I/O) model used in Linux is synchronous I/O. After a request is made in this model, the application blocks until the request is satisfied. This is a great paradigm because the calling application requires no central processing
developer.ibm.com
sync와 async, blocking과 non-blocking 차이점은?
오늘 수업 시간에 다룬 내용이다. 나도 이 이 차이점에 대해 명확하지 않다. 나도 확실하지 않지만 학생들을 믿고 던졌다. 각각의 차이점을 이해하기 위해 던진 링크는 Boost application performance usin
www.slipp.net
코드로만 개발했던거에 대해
개념을 이해하려니까 놓친 부분이 많았다
이제라도 알면 돼

'BackEnd > C랑 C++' 카테고리의 다른 글
Boost-echo tcp client & server를 살펴보자 (1) (2) | 2021.06.11 |
---|---|
C++ 스마트 포인터를 알아보자 - (1) unique_ptr (0) | 2021.05.25 |
C++ 클래스 멤버변수 초기화를 알아보자 (4) | 2021.03.25 |
C++ 대문자/소문자 변환 (4) | 2021.02.26 |
C++ Function Object (함수 객체)를 알아보자 (4) | 2021.01.22 |