[Webserver]miniWebserver – (2)

πŸ‘‰πŸ» 이전 μ½”λ“œμ—μ„œ 가독성을 μœ„ν•΄μ„œ νŒŒμΌμ„ λΆ„λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.
I separated the files in the previous code for readability.

πŸ‘‰πŸ» Main.cppμ—μ„œ μ„œλ²„ μ‹œμž‘λΆ€λΆ„μ„ μ“°λ ˆλ“œλ‘œ λΆ„λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.
I separated the server startup part into a thread in Main.cpp.

πŸ‘‰πŸ» β—οΈλ‘œ ν‘œμ‹œλœ 뢀뢄은 κΈ°μ‘΄μ½”λ“œμ—μ„œ 바뀐 λ‚΄μš©μž…λ‹ˆλ‹€.
The parts marked with ❗️ are the changes made to the existing code.

βœ”οΈ Server.h

#include <string>

class Server {
public:
    // μƒμ„±μž: μ„œλ²„ μ΄ˆκΈ°ν™” (IP, Port)
    // Constructor: Initialize server (IP, Port)
    Server(const std::string& ip, int port);

    // μ„œλ²„ μ†ŒμΌ“ 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό μ €μž₯ν•  멀버 λ³€μˆ˜
    // Member variable to store server socket file descriptors
    int server_fd = -1; // ❗️

    // μ„œλ²„ μ‹€ν–‰ ν•¨μˆ˜: λ¦¬μŠ€λ‹μ„ μ‹œμž‘ν•˜κ³  메인 루프λ₯Ό 돌림
    // Server execution function: Starts listening and runs the main loop
    bool start();

    // μ„œλ²„ μ’…λ£Œ (ν•„μš”ν•˜λ‹€λ©΄)
    // Shut down the server (if necessary)
    void stop();

private:
    std::string ip_address;
    int port_number;

    // μ‹€μ œ λ¦¬μŠ€λ‹ 및 μ—°κ²° μˆ˜μ‹  둜직 (λ‚΄λΆ€ κ΅¬ν˜„)
    // Actual listening and connection reception logic (internal implementation)
    void run_listener();

    // μ—°κ²° 처리 둜직 (μŠ€λ ˆλ“œ ν’€ λ˜λŠ” 별도 ν•Έλ“€λŸ¬μ—κ²Œ μœ„μž„)
    // Connection handling logic (delegated to thread pool or separate handler)
    void handle_client(int client_socket);
};

βœ”οΈ Server.cpp

#include "Server.h"
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <thread>
#include <vector>

//#define PORT 5080 ❗️
#define BACKLOG 10 // λŒ€κΈ° 큐 크기 / wait queue size

Server::Server(const std::string& ip, int port)
    : ip_address(ip), port_number(port) {}

void Server::run_listener() {
    //std::cout << "[INFO] Server listening on " << ip_address << ":" << port_number << std::endl;

    // --- 1. μ†ŒμΌ“ 생성 (Socket Creation) --- //
    //int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    // 멀버 λ³€μˆ˜λ‘œ λ³€κ²½ / Change to member variable❗️
    server_fd = socket(AF_INET, SOCK_STREAM, 0);

    //if (server_fd == 0) { ❗️
    if (server_fd == -1) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    int opt = 1;
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    // --- 2. 바인딩 (Binding) --- //
    struct sockaddr_in address;
    address.sin_family = AF_INET;         // IPv4
    address.sin_addr.s_addr = INADDR_ANY; // λͺ¨λ“  μΈν„°νŽ˜μ΄μŠ€ IP μ‚¬μš© / Use all interface IPs
    address.sin_port = htons(port_number);       // 포트λ₯Ό λ„€νŠΈμ›Œν¬ λ°”μ΄νŠΈ μˆœμ„œλ‘œ λ³€ν™˜ / Convert ports to network byte order

    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
        perror("bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // --- 3. λ¦¬μŠ€λ‹ μ‹œμž‘ (setsockopt: listen_socket(socket)) --- //
    if (listen(server_fd, BACKLOG) < 0) {
        perror("listen failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }
    std::cout << "Server listening on port " << port_number << "..." << std::endl;

    // --- 4. 메인 루프 (Accept loop): ν΄λΌμ΄μ–ΈνŠΈ 연결을 λ¬΄ν•œνžˆ κΈ°λ‹€λ¦Ό / Infinitely waiting for client connections --- //
    while (true) {

        // IPv4ꡬ쑰체 / Structure for IPv4 address
        struct sockaddr_in client_address;

        // μ£Όμ†Œ ꡬ쑰체가 λ©”λͺ¨λ¦¬μ—μ„œ μ°¨μ§€ν•˜λŠ” λ°”μ΄νŠΈ 크기λ₯Ό μ €μž₯ν•˜λŠ” λ³€μˆ˜μ˜ νƒ€μž…
        // Type of the variable storing the byte size occupied by the address structure in memory
        socklen_t addrlen = sizeof(client_address);

        // accept() ν•¨μˆ˜κ°€ λΈ”λ‘œν‚Ή(Blocking) μƒνƒœμ—μ„œ ν΄λΌμ΄μ–ΈνŠΈ 연결을 κΈ°λ‹€λ¦Ό
        // The accept() function waits for client connections in a blocking state.
        int client_socket = accept(server_fd, (struct sockaddr *)&client_address, &addrlen);
        if (client_socket < 0) {
            perror("accept failed");
            continue; // μ‹€νŒ¨ν–ˆμœΌλ©΄ λ‹€μŒ 루프λ₯Ό 돌며 μž¬μ‹œλ„ / Retry in the next loop if it fails
        }

        // λ™μ‹œμ„± 처리: μƒˆλ‘œμš΄ 연결이 였면 μƒˆλ‘œμš΄ μ“°λ ˆλ“œλ₯Ό μƒμ„±ν•˜μ—¬ 처리
        // Concurrency Handling: When a new connection arrives, create a new thread to handle it.
        //std::thread client_thread(Server::handle_client, client_socket);
        //
        // λžŒλ‹€ {[](){...}} λŠ” ν˜„μž¬ μŠ€λ ˆλ“œκ°€ Server 객체(this)의 λ§₯락을 μœ μ§€ν•˜λ©° μ‹€ν–‰λ˜λ„λ‘ 보μž₯ν•©λ‹ˆλ‹€.
        // Lambda {[](){...}} ensures that the current thread executes while maintaining the context of the Server object (this).
        std::thread client_thread([this, client_socket]() {
            this->handle_client(client_socket);
        });
        // μ“°λ ˆλ“œλ₯Ό λΆ„λ¦¬ν•˜κ³  메인 루프λ₯Ό 계속 μ‹€ν–‰ν•˜κ²Œ 함
        // Detach the thread so that the main loop continues running.
        client_thread.detach();
    }
}
//❗️
void Server::handle_client(int client_socket) {
    char buffer[1024];
    std::cout << "[Connection] New client connected." << std::endl;

    // 1. 데이터 μˆ˜μ‹  / receive data (Read)
    ssize_t bytes_read = read(client_socket, buffer, sizeof(buffer) - 1);
    if (bytes_read > 0) {
        buffer[bytes_read] = '\0'; // C-string
        std::string request = buffer;
        std::cout << "[Received] " << request.substr(0, 30) << "..." << std::endl;

        // 2. 응닡 전솑 / send response(Write)
        const char* response = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<h1>πŸ‘‹Hi~ Hi~ Hi~~~!</h1><p>Welcome to the miniWebserver!</p>";
        write(client_socket, response, strlen(response));
    }

    // 3. μ—°κ²° μ’…λ£Œ / close connection
    close(client_socket);
    std::cout << "[Connection] Client disconnected." << std::endl;
}

bool Server::start() {
    // μ„œλ²„κ°€ μ‹€μ œλ‘œ μž‘λ™ν•˜λŠ” 메인 ν•¨μˆ˜ / Main function that actually runs the server
    try {
        run_listener();
    } catch (const std::exception& e) {
        std::cerr << "[ERROR] Server failed to start: " << e.what() << std::endl;
        return false;
    }
    return true;
}

void Server::stop() {
    // μ„œλ²„λ₯Ό μ’…λ£Œν•˜λŠ” ν•¨μˆ˜ / Function to stop the server
    //close(server_fd);❗️
    if (server_fd != -1) {
        // 멀버 λ³€μˆ˜μ— μ €μž₯된 값을 μ‚¬μš©ν•˜μ—¬ μ’…λ£Œν•©λ‹ˆλ‹€. / Use the value stored in the member variable to stop the server
        close(server_fd);
        server_fd = -1; // λ‹«μ•˜μœΌλ―€λ‘œ μ΄ˆκΈ°ν™” / Initialize to -1 after closing
    }
}

βœ”οΈ Main.cpp

#include "Server.h"
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
#include <string>

int main() {

    // 1. μ„œλ²„ μΈμŠ€ν„΄μŠ€ 생성 / Create server instance
    Server my_server("127.0.0.1", 5080); 
    std::thread server_thread(&Server::start, &my_server); // μ„œλ²„ μ‹œμž‘ / Start server ❗️
    std::cout << "--- Starting Server Application ---" << std::endl;

    // 2. μ’…λ£Œ 둜직 (μ„œλ²„κ°€ μ’…λ£Œλ˜μ§€ μ•ŠκΈ° 떄문에 μ‹€ν–‰λ˜μ§€ μ•ŠμŒ,λ‚˜μ€‘μ„ μœ„ν•œ μ’…λ£Œ 둜직)
    // Termination logic (not executed because the server has not terminated, termination logic for later)
    server_thread.join();
    my_server.stop();

    return 0;
}

βœ”οΈ 컴파일 / Compiling

g++ Server.cpp Main.cpp -o server -std=c++17 -pthread

βœ”οΈμ‹€ν–‰ / Run

./server

Leave a Reply