본문 바로가기
-----ETC-----/C++ 네트워크 프로그래밍 시리즈

[C++ 네트워크 프로그래밍] Day 14: 웹 소켓 서버 개발

by cogito21_cpp 2024. 8. 1.
반응형

웹 소켓(WebSocket) 서버 개발

웹 소켓 서버는 클라이언트와의 양방향 실시간 통신을 위해 웹 소켓 연결을 설정하고, 데이터를 송수신하는 프로그램입니다. 웹 소켓 서버는 주로 실시간 데이터 스트리밍, 채팅 애플리케이션 등에서 사용됩니다.

웹 소켓 서버의 주요 기능

  1. 연결 수락: 클라이언트의 웹 소켓 연결을 수락합니다.
  2. 데이터 송신: 클라이언트로 데이터를 전송합니다.
  3. 데이터 수신: 클라이언트로부터 데이터를 수신합니다.
  4. 연결 종료: 클라이언트와의 웹 소켓 연결을 종료합니다.

Boost.Beast를 이용한 웹 소켓 서버 구현

Boost.Beast는 HTTP 및 웹 소켓 프로토콜을 지원하는 라이브러리로, Boost.Asio를 기반으로 합니다. Boost.Beast를 사용하여 간단한 웹 소켓 서버를 구현할 수 있습니다.

웹 소켓 서버 코드 예제

AsyncWebSocketServer.h

#ifndef ASYNCWEBSOCKETSERVER_H
#define ASYNCWEBSOCKETSERVER_H

#include <boost/beast/core.hpp>
#include <boost/beast/websocket.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 <iostream>

namespace beast = boost::beast; // Boost.Beast 네임스페이스
namespace websocket = beast::websocket; // 웹 소켓 관련 네임스페이스
namespace net = boost::asio; // Boost.Asio 네임스페이스
using tcp = net::ip::tcp; // TCP 네임스페이스

// 세션 클래스 정의
class session : public std::enable_shared_from_this<session> {
    websocket::stream<tcp::socket> ws_; // 웹 소켓 스트림
    beast::flat_buffer buffer_; // 데이터 버퍼

public:
    // 생성자
    explicit session(tcp::socket socket)
        : ws_(std::move(socket)) {}

    // 세션 실행
    void run() {
        ws_.async_accept(
            [self = shared_from_this()](beast::error_code ec) {
                if (!ec) {
                    self->do_read();
                }
            });
    }

private:
    // 데이터 읽기
    void do_read() {
        ws_.async_read(buffer_,
            [self = shared_from_this()](beast::error_code ec, std::size_t bytes_transferred) {
                boost::ignore_unused(bytes_transferred);
                if (!ec) {
                    self->do_write();
                }
            });
    }

    // 데이터 쓰기
    void do_write() {
        ws_.text(ws_.got_text());
        ws_.async_write(buffer_.data(),
            [self = shared_from_this()](beast::error_code ec, std::size_t bytes_transferred) {
                boost::ignore_unused(bytes_transferred);
                if (!ec) {
                    self->buffer_.consume(self->buffer_.size());
                    self->do_read();
                }
            });
    }
};

// 리스너 클래스 정의
class listener : public std::enable_shared_from_this<listener> {
    net::io_context& ioc_; // IO 컨텍스트
    tcp::acceptor acceptor_; // TCP 수신자

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() {
        acceptor_.async_accept(
            net::make_strand(ioc_),
            [self = shared_from_this()](beast::error_code ec, tcp::socket socket) {
                if (!ec) {
                    std::make_shared<session>(std::move(socket))->run();
                }
                self->do_accept();
            });
    }
};

#endif // ASYNCWEBSOCKETSERVER_H

 

main.cpp

#include <iostream>
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.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 "AsyncWebSocketServer.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를 사용하여 간단한 웹 소켓 서버를 구현한 예제입니다. 이 서버는 클라이언트와의 웹 소켓 연결을 수락하고, 데이터를 주고받는 기능을 제공합니다.

  • session 클래스:
    • session 클래스는 클라이언트와의 세션을 관리합니다. 웹 소켓 스트림을 사용하여 클라이언트와 데이터를 주고받습니다.
    • run() 함수는 웹 소켓 연결을 수락합니다.
    • do_read() 함수는 클라이언트로부터 데이터를 비동기적으로 읽습니다.
    • do_write() 함수는 클라이언트에게 데이터를 비동기적으로 씁니다.
  • listener 클래스:
    • listener 클래스는 클라이언트의 연결을 수락합니다. TCP 수신자를 사용하여 연결을 수락하고, 새로운 session 객체를 생성합니다.
    • do_accept() 함수는 비동기적으로 클라이언트의 연결을 수락합니다.

이제 열네 번째 날의 학습을 마쳤습니다. 웹 소켓의 기본 개념과 Boost.Beast를 사용하여 간단한 웹 소켓 서버를 구현하는 방법을 학습했습니다.

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "멀티스레드 서버 개발 (Boost.Asio)"에 대해 학습하겠습니다.

반응형