반응형
C++에서 비동기 TCP 서버를 구현할 때 많이 사용되는 ACE와 Boost Asio는 각기 다른 패턴을 지원하는데, 특히 Proactor 패턴 구현에서 차이가 두드러집니다. 아래에서는 두 라이브러리의 Proactor 패턴 차이점과 간단한 예제 코드를 비교하여 살펴보겠습니다.
-
기본 구조 및 설계 철학
- ACE Proactor: ACE는 Proactor 패턴을 완벽하게 구현합니다. 주로 비동기 작업이 완료 이벤트를 발생시킬 때 핸들러를 실행하며, 운영체제의 비동기 API를 활용하여 작업 완료 후 이벤트가 트리거되는 구조입니다.
- Boost Asio: Boost Asio는 Reactor 패턴을 기본으로 동작하며, 운영체제의 비동기 I/O API와 결합하여 Proactor 패턴과 유사하게 비동기 처리를 지원합니다. Boost Asio는 작업이 등록되면 핸들러를 바로 호출하는 방식으로 동작하는 경우가 많아 ACE의 Proactor와 차이가 있습니다.
-
OS 종속성
- ACE는 Windows에서
IOCP
, Unix에서aio_*
함수 등 특정 OS의 비동기 API에 의존하여 Proactor 패턴을 구현합니다. - Boost Asio는 플랫폼 독립적으로 설계되어 있어 운영체제에 맞는 비동기 API를 추상화하여 제공됩니다.
- ACE는 Windows에서
아래 예제에서는 ACE의 Proactor
를 사용하여 비동기 TCP 서버를 구현하는 방식입니다.
cpp
#include <ace/Proactor.h>
#include <ace/Asynch_IO.h>
#include <ace/SOCK_Acceptor.h>
#include <ace/INET_Addr.h>
class MyHandler : public ACE_Service_Handler {
public:
void open(ACE_HANDLE new_handle, ACE_Message_Block &message_block) override {
ACE_SOCK_Stream peer(new_handle);
ACE_Asynch_Read_Stream asynch_reader_;
asynch_reader_.open(*this, new_handle);
ACE_Message_Block *mb;
ACE_NEW_NORETURN(mb, ACE_Message_Block(4096));
asynch_reader_.read(*mb, mb->size());
}
void handle_read_stream(const ACE_Asynch_Read_Stream::Result &result) override {
if (!result.success() || result.bytes_transferred() == 0) {
delete this;
return;
}
result.message_block().rd_ptr()[result.bytes_transferred()] = '\0';
ACE_DEBUG((LM_DEBUG, ACE_TEXT("Data received: %s\n"), result.message_block().rd_ptr()));
}
};
int main() {
ACE_INET_Addr listen_addr(8080);
ACE_Asynch_Acceptor<MyHandler> acceptor;
acceptor.open(listen_addr);
ACE_Proactor::instance()->proactor_run_event_loop();
return 0;
}
Boost Asio는 Proactor 패턴을 직접 지원하지 않지만, 비동기 작업 등록 및 콜백을 통해 비슷한 방식으로 구현할 수 있습니다.
cpp
#include <boost/asio.hpp>
#include <iostream>
using boost::asio::ip::tcp;
class Session : public std::enable_shared_from_this<Session> {
public:
explicit Session(tcp::socket socket) : socket_(std::move(socket)) {}
void start() { read(); }
private:
void read() {
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length) {
if (!ec) {
std::cout << "Data received: " << data_ << std::endl;
write(length);
}
});
}
void write(std::size_t length) {
auto self(shared_from_this());
boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/) {
if (!ec) {
read();
}
});
}
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class Server {
public:
Server(boost::asio::io_context& io_context, short port)
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port)) {
accept();
}
private:
void accept() {
acceptor_.async_accept([this](boost::system::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<Session>(std::move(socket))->start();
}
accept();
});
}
tcp::acceptor acceptor_;
};
int main() {
try {
boost::asio::io_context io_context;
Server server(io_context, 8080);
io_context.run();
}
catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
return 0;
}
- ACE는 Proactor 패턴을 완전하게 구현하여 OS의 비동기 I/O API에 따라 동작합니다. Proactor 패턴을 완전하게 구현하고자 할 때는 ACE가 더 적합할 수 있습니다.
- Boost Asio는 플랫폼 독립적이며 Reactor 패턴을 사용하지만, 운영체제의 비동기 API와 결합하여 Proactor와 유사하게 동작하게 할 수 있습니다.
728x90
반응형
'C C++' 카테고리의 다른 글
C++20 우주선 연산자 spaceship operator (0) | 2024.11.13 |
---|---|
C++ Boost의 날짜,시간 및 ACE의 날짜,시간 (0) | 2024.11.12 |
C++ ACE와 Boost 쓰레드 동시 사용 방법과 주의 사항 (0) | 2024.11.12 |
Get platform bus type (32/64bit) (0) | 2024.11.09 |
로-메이저(Row-major) 접근 방식: 메모리 접근 최적화를 위한 배열 배치 방법 (0) | 2024.11.08 |