반응형
Boost.Beast 소개
Boost.Beast는 HTTP와 WebSocket 프로토콜을 지원하는 라이브러리입니다. Boost.Asio를 기반으로 하여 비동기 네트워크 프로그래밍을 쉽게 구현할 수 있습니다. HTTP 서버를 개발하기 위해 Boost.Beast를 사용하면 간단하고 효율적인 코드 작성을 할 수 있습니다.
Boost.Beast 설치
Boost 라이브러리 설치는 이전 단계에서 다루었으며, Boost.Beast는 Boost 라이브러리의 일부입니다.
HTTP 서버 구현
HTTP 서버를 구현하기 위해 다음과 같은 단계를 따릅니다:
- io_context: 비동기 작업을 관리하는 io_context 객체를 생성합니다.
- tcp::acceptor: 클라이언트 연결을 수락하기 위한 acceptor 객체를 생성합니다.
- 세션 관리: 클라이언트 연결을 처리하기 위한 세션 객체를 생성합니다.
- 요청 처리: 클라이언트의 HTTP 요청을 처리하고, 응답을 생성합니다.
HTTP 서버 코드 예제
AsyncHttpServer.h
#ifndef ASYNCHTTPSERVER_H
#define ASYNCHTTPSERVER_H
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <boost/config.hpp>
#include <memory>
#include <string>
#include <thread>
#include <vector>
namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
// 세션 클래스
class session : public std::enable_shared_from_this<session> {
tcp::socket socket_;
beast::flat_buffer buffer_;
http::request<http::string_body> req_;
public:
explicit session(tcp::socket socket) : socket_(std::move(socket)) {}
void run() {
do_read();
}
private:
void do_read() {
auto self = shared_from_this();
http::async_read(socket_, buffer_, req_,
[self](beast::error_code ec, std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);
if (!ec) {
self->handle_request();
}
});
}
void handle_request() {
auto const bad_request = [&req = req_](beast::string_view why) {
http::response<http::string_body> res{ http::status::bad_request, req.version() };
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.keep_alive(req.keep_alive());
res.body() = std::string(why);
res.prepare_payload();
return res;
};
if (req_.method() != http::verb::get && req_.method() != http::verb::head) {
return do_write(bad_request("Unknown HTTP-method"));
}
if (req_.target() != "/index.html") {
return do_write(bad_request("Illegal request-target"));
}
http::string_body::value_type body = "Hello, World!";
auto const size = body.size();
http::response<http::string_body> res{
std::piecewise_construct,
std::make_tuple(std::move(body)),
std::make_tuple(http::status::ok, req_.version())
};
res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
res.set(http::field::content_type, "text/html");
res.content_length(size);
res.keep_alive(req_.keep_alive());
return do_write(std::move(res));
}
void do_write(http::response<http::string_body>&& res) {
auto self = shared_from_this();
http::async_write(socket_, res,
[self](beast::error_code ec, std::size_t bytes_transferred) {
self->socket_.shutdown(tcp::socket::shutdown_send, ec);
self->socket_.close();
});
}
};
// 리스너 클래스
class listener : public std::enable_shared_from_this<listener> {
net::io_context& ioc_;
tcp::acceptor acceptor_;
public:
listener(net::io_context& ioc, tcp::endpoint endpoint)
: ioc_(ioc), acceptor_(net::make_strand(ioc)) {
beast::error_code ec;
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
std::cerr << "오류: " << ec.message() << std::endl;
return;
}
acceptor_.set_option(net::socket_base::reuse_address(true), ec);
if (ec) {
std::cerr << "오류: " << ec.message() << std::endl;
return;
}
acceptor_.bind(endpoint, ec);
if (ec) {
std::cerr << "오류: " << ec.message() << std::endl;
return;
}
acceptor_.listen(net::socket_base::max_listen_connections, ec);
if (ec) {
std::cerr << "오류: " << ec.message() << std::endl;
return;
}
do_accept();
}
private:
void do_accept() {
auto self = shared_from_this();
acceptor_.async_accept(
net::make_strand(ioc_),
[self](beast::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->run();
}
self->do_accept();
});
}
};
#endif // ASYNCHTTPSERVER_H
main.cpp
#include <iostream>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/strand.hpp>
#include <boost/config.hpp>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include "AsyncHttpServer.h"
namespace net = boost::asio;
using tcp = net::ip::tcp;
int main() {
try {
auto const address = net::ip::make_address("0.0.0.0");
auto const port = static_cast<unsigned short>(std::atoi("12345"));
net::io_context ioc{ 1 };
std::make_shared<listener>(ioc, tcp::endpoint{ address, port })->run();
ioc.run();
}
catch (std::exception const& e) {
std::cerr << "예외 발생: " << e.what() << std::endl;
}
return EXIT_SUCCESS;
}
설명
위의 코드는 Boost.Beast를 사용하여 간단한 HTTP 서버를 구현한 예제입니다. 이 서버는 클라이언트의 요청을 비동기적으로 처리하고, 정적 HTML 페이지를 반환합니다.
- listener 클래스:
listener
클래스는 클라이언트의 연결을 수락합니다.tcp::acceptor
를 사용하여 연결을 수락하고, 새로운session
객체를 생성합니다.do_accept()
함수는 비동기적으로 클라이언트의 연결을 수락합니다.
- session 클래스:
session
클래스는 클라이언트와의 세션을 관리합니다.tcp::socket
을 사용하여 클라이언트와 데이터를 송수신합니다.do_read()
함수는 클라이언트의 HTTP 요청을 비동기적으로 읽습니다.handle_request()
함수는 클라이언트의 HTTP 요청을 처리하고, 적절한 HTTP 응답을 생성합니다.do_write()
함수는 클라이언트에게 HTTP 응답을 비동기적으로 씁니다.
이제 열 번째 날의 학습을 마쳤습니다. Boost.Beast를 사용하여 HTTP 서버를 구현하는 방법을 학습했습니다.
질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "RESTful API 설계와 구현"에 대해 학습하겠습니다.
반응형
'-----ETC----- > C++ 네트워크 프로그래밍 시리즈' 카테고리의 다른 글
[C++ 네트워크 프로그래밍] Day 12: 웹 소켓 프로그래밍 기초 (0) | 2024.08.01 |
---|---|
[C++ 네트워크 프로그래밍] Day 13: 웹 소켓 클라이언트 개발 (0) | 2024.08.01 |
[C++ 네트워크 프로그래밍] Day 11: RESTful API 설계와 구현 (0) | 2024.08.01 |
[C++ 네트워크 프로그래밍] Day 9: HTTP 클라이언트 개발 (libcurl) (0) | 2024.08.01 |
[C++ 네트워크 프로그래밍] Day 7: 네트워크 디버깅 기법 (0) | 2024.08.01 |