Асинхронный веб-сервер 001

Ответить
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Асинхронный веб-сервер 001

Сообщение ya »

server_http_class-001.cpp

Код: Выделить всё

#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <mutex>
#include <arpa/inet.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <unistd.h>  
#include <netinet/in.h>  
#include <cstdlib>

#define PORT 8090
#define BACKLOG 10
#define BUFFER_SIZE 1024

class HttpServer {
public:
    void start();
private:
    void listenForConnections();
    void handleClient(int clientSocket);
    std::string processHttpRequest(const std::string& request);
    std::string createRedirectResponse(const std::string& host);
};

// start() Основная точка входа для запуска сервера.
void HttpServer::start() {
    listenForConnections();
}

// listenForConnections() Метод для прослушивания соединений и передачи их на обработку.
void HttpServer::listenForConnections() {
    int serverFd;
    struct sockaddr_in address;
    int opt = 1;
    int addrLen = sizeof(address);

    if ((serverFd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("Set socket options failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

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

    if (listen(serverFd, BACKLOG) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    std::cout << "Server is listening on port " << PORT << std::endl;

    while (true) {
        int newSocket = accept(serverFd, (struct sockaddr *)&address, (socklen_t*)&addrLen);
        if (newSocket < 0) {
            perror("Accept failed");
            continue;
        }

        // Передаем управление новым клиентом в отдельный поток
        std::thread t(&HttpServer::handleClient, this, newSocket);
        t.detach();
    }
}

// handleClient() Метод для обработки клиента в отдельном потоке.
void HttpServer::handleClient(int clientSocket) {
    char buffer[BUFFER_SIZE];
    ssize_t bytesRead = recv(clientSocket, buffer, BUFFER_SIZE, 0);
    if (bytesRead <= 0) {
        std::cerr << "Error reading from socket" << std::endl;
        close(clientSocket);
        return;
    }

    std::string request(buffer, bytesRead);
    std::cout << "Received request:\n" << request << std::endl;

    // Обрабатываем запрос
    std::string response = processHttpRequest(request);

    if (!response.empty()) {
        send(clientSocket, response.c_str(), response.length(), 0);        
    } else {
        // Если ответ пустой, отправляем ошибку
        std::string errorResponse = "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n";
        send(clientSocket, errorResponse.c_str(), errorResponse.length(), 0);   
    }

    close(clientSocket); // Закрываем соединение
}

// processHttpRequest() Метод для обработки HTTP-запроса.
std::string HttpServer::processHttpRequest(const std::string& request) {
    if (request.substr(0, 3) == "GET") {
        size_t pos = request.find("Host:");
        if (pos != std::string::npos) {
            size_t endPos = request.find("\r\n", pos);
//             if (endPos != std::string::npos) {
//                 std::string host = request.substr(pos + 6, endPos - pos - 6);
//                 return createRedirectResponse(host);
//             }
        }
    }

    // Здесь можно добавить обработку других типов запросов (POST, PUT и т.д.)
    return "";
}

// createRedirectResponse() Метод для создания ответа о перенаправлении на HTTPS.
std::string HttpServer::createRedirectResponse(const std::string& host) {
    return "HTTP/1.1 301 Moved Permanently\r\n"
           "Location: http://" + host + "\r\n"
           "Connection: close\r\n"
           "\r\n";
}

// Запуск сервера
int main() {
    HttpServer server;
    server.start();
    return 0;
}
Компиляция:

Код: Выделить всё

g++ -std=c++11 -o server_http_class-001 server_http_class-001.cpp -pthread
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Re: Асинхронный веб-сервер 003

Сообщение ya »

server_http_class-003.cpp

Код: Выделить всё

#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <mutex>
#include <arpa/inet.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <unistd.h>  
#include <netinet/in.h>  
#include <cstdlib>

#define PORT 8090
#define BACKLOG 10
#define BUFFER_SIZE 1024

class View {
public:
    virtual std::string render() const = 0;
};

class HomePage : public View {
public:
    std::string render() const override {
        return "<!DOCTYPE html>\n<html><head><title>Home Page</title></head>"
               "<body><h1>Welcome to the Home Page!</h1></body></html>";
    }
};

class AboutPage : public View {
public:
    std::string render() const override {
        return "<!DOCTYPE html>\n<html><head><title>About Us</title></head>"
               "<body><h1>About Us</h1><p>This is our about page.</p></body></html>";
    }
};

class Controller {
public:
    virtual std::string handleRequest(const std::string& path) = 0;
};

class MainController : public Controller {
public:
    std::string handleRequest(const std::string& path) override {
        if (path == "/") {
            HomePage homePage;
            return homePage.render();
        } else if (path == "/about") {
            AboutPage aboutPage;
            return aboutPage.render();
        } else {
            return "HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\nContent-Length: 0\r\n\r\n";
        }
    }
};

class HttpServer {
public:
    void start();
private:
    void listenForConnections();
    void handleClient(int clientSocket);
    std::string processHttpRequest(const std::string& request);
    std::string getPathFromRequest(const std::string& request);
};

// start() Основная точка входа для запуска сервера.
void HttpServer::start() {
    listenForConnections();
}

// listenForConnections() Метод для прослушивания соединений и передачи их на обработку.
void HttpServer::listenForConnections() {
    int serverFd;
    struct sockaddr_in address;
    int opt = 1;
    int addrLen = sizeof(address);

    if ((serverFd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("Set socket options failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

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

    if (listen(serverFd, BACKLOG) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    std::cout << "Server is listening on port " << PORT << std::endl;

    while (true) {
        int newSocket = accept(serverFd, (struct sockaddr *)&address, (socklen_t*)&addrLen);
        if (newSocket < 0) {
            perror("Accept failed");
            continue;
        }

        // Передаем управление новым клиентом в отдельный поток
        std::thread t(&HttpServer::handleClient, this, newSocket);
        t.detach();
    }
}

// handleClient() Метод для обработки клиента в отдельном потоке.
void HttpServer::handleClient(int clientSocket) {
    char buffer[BUFFER_SIZE];
    ssize_t bytesRead = recv(clientSocket, buffer, BUFFER_SIZE, 0);
    if (bytesRead <= 0) {
        std::cerr << "Error reading from socket" << std::endl;
        close(clientSocket);
        return;
    }

    std::string request(buffer, bytesRead);
    std::cout << "Received request:\n" << request << std::endl;

    // Обрабатываем запрос
    std::string response = processHttpRequest(request);

    if (!response.empty()) {
        // Формируем полный HTTP-ответ
        std::string fullResponse = "HTTP/1.1 200 OK\r\n"
                                   "Content-Type: text/html\r\n"
                                   "Content-Length: " + std::to_string(response.length()) + "\r\n"
                                   "Connection: close\r\n"
                                   "\r\n"
                                   + response;

        send(clientSocket, fullResponse.c_str(), fullResponse.length(), 0);        
    } else {
        // Если ответ пустой, отправляем ошибку
        std::string errorResponse = "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n";
        send(clientSocket, errorResponse.c_str(), errorResponse.length(), 0);   
    }

    close(clientSocket); // Закрываем соединение
}

// processHttpRequest() Метод для обработки HTTP-запроса.
std::string HttpServer::processHttpRequest(const std::string& request) {
    std::string path = getPathFromRequest(request);
    MainController controller;
    return controller.handleRequest(path);
}

// getPathFromRequest() Метод для извлечения пути из HTTP-запроса.
std::string HttpServer::getPathFromRequest(const std::string& request) {
    size_t pos = request.find(' ');
    if (pos != std::string::npos) {
        size_t endPos = request.find(' ', pos + 1);
        if (endPos != std::string::npos) {
            return request.substr(pos + 1, endPos - pos - 1);
        }
    }
    return "/"; // Возвращаем корневой путь по умолчанию
}

// Запуск сервера
int main() {
    HttpServer server;
    server.start();
    return 0;
}
Компиляция

Код: Выделить всё

g++ -std=c++11 -o server_http_class-003 server_http_class-003.cpp -pthread
ya
^-^
Сообщения: 2336
Зарегистрирован: 16 дек 2021, 19:56

Re: Асинхронный веб-сервер 005

Сообщение ya »

server_http_class-005.cpp

Код: Выделить всё

#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <mutex>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <unordered_map>// Для std::unordered_map
#include <optional>  // Для std::optional
#include <map>
#include <mysql_connection.h>
#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <memory> // Для std::unique_ptr
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>



#define PORT 8090
#define BACKLOG 10
#define BUFFER_SIZE 1024

#define HOSTDB "localhost"
#define USERDB "phpmyadmin"
#define PASSDB "1"
#define DBNAME "http004"

class View {
public:
    virtual std::string render() const = 0;
};

class HomePage : public View {
public:
    std::string render() const override {
        return "<!DOCTYPE html>\n<html><head><title>Home Page</title></head>"
               "<body><h1>Welcome to the Home Page!</h1></body></html>";
    }
};

class AboutPage : public View {
public:
    std::string render() const override {
        return "<!DOCTYPE html>\n<html><head><title>About Us</title></head>"
               "<body><h1>About Us</h1><p>This is our about page.</p></body></html>";
    }
};

class GuestBookForm : public View {
public:
    std::string render() const override {
        return R"(<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Гостевая книга</title>
</head>
<body>
    <h1 style="text-align: center;">Добро пожаловать в нашу гостевую книгу!</h1>
    <form method="post" action="/guestbook">
        <label for="name">Ваше имя:</label>
        <input type="text" id="name" name="name"><br>
        <textarea rows="5" cols="50" name="message"></textarea><br>
        <button type="submit">Отправить сообщение</button>
    </form>
</body>
</html>)";
    }
};

class Controller {
public:
    sql::Driver* driver; // Указатель на драйвер
    std::unique_ptr<sql::Connection> con;

    Controller() {
        try {
            driver = get_driver_instance(); // Просто присваиваем указателю
            con.reset(driver->connect(HOSTDB, USERDB, PASSDB));
            con->setSchema(DBNAME);
        } catch (sql::SQLException &e) {
            std::cerr << "SQL Exception: " << e.what() << std::endl;
            exit(EXIT_FAILURE);
        }
    }

    ~Controller() {
        // Не нужно освобождать driver, так как он управляется библиотекой
    }

    virtual std::string handleRequest(const std::string& path) = 0;


protected:
    bool saveMessageToDatabase(const std::map<std::string, std::string>& params) {
        std::string name = params.at("name");
        std::string message = params.at("message");
        std::string query = "INSERT INTO guestbook (name, message, datetime_create	) VALUES ('" + name + "', '" + message + "' , NOW() ) ";

     try {
        std::unique_ptr<sql::PreparedStatement> prep_stmt(con->prepareStatement(query));
        prep_stmt->setString(1, name);
        prep_stmt->setString(2, message);
        prep_stmt->execute();
        return true;
    } catch(sql::SQLException& e) {
        std::cerr << "Error saving message: " << e.what() << '\n';
        return false;
    }
    };
};

class GuestBookController : public Controller {
public:
    std::string handleRequest(const std::string& path) override {
        if (path == "/guestbook") {
            // Логика обработки запроса на гостевую книгу
            return GuestBookForm().render();
        }
        // Обработка других маршрутов
        return HomePage().render(); // например
    }
};


// Изменение на уровне класса HttpServer
class HttpServer {
public:
    void start();
private:
    void listenForConnections();
    void handleClient(int clientSocket);
    std::string processHttpRequest(const std::string& request);
    std::string getPathFromRequest(const std::string& request);
};

void HttpServer::start() {
    listenForConnections();
}

void HttpServer::listenForConnections() {
    int serverFd;
    struct sockaddr_in address;
    int opt = 1;
    socklen_t addrLen = sizeof(address);

    if ((serverFd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
        perror("Socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
        perror("Set socket options failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

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

    if (listen(serverFd, BACKLOG) < 0) {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    std::cout << "Server is listening on port " << PORT << std::endl;

    while (true) {
        int newSocket = accept(serverFd, nullptr, 0);
        if (newSocket < 0) {
            perror("Accept failed");
            continue;
        }

        std::thread([this, newSocket] {
            handleClient(newSocket);
        }).detach();
    }
}

// handleClient() Метод для обработки клиента в отдельном потоке.
void HttpServer::handleClient(int clientSocket) {
    char buffer[BUFFER_SIZE];
    ssize_t bytesRead = recv(clientSocket, buffer, BUFFER_SIZE, 0);
    if(bytesRead <= 0) {
        std::cerr << "Error reading from socket" << std::endl;
        close(clientSocket);
        return;
    }

    std::string request(buffer, bytesRead);
    std::cout << "Received request:\n" << request << std::endl;

    // Обрабатываем запрос
    std::string response = processHttpRequest(request);

    if (!response.empty()) {
        // Формируем полный HTTP-ответ
        std::string fullResponse = "HTTP/1.1 200 OK\r\n"
                                   "Content-Type: text/html\r\n"
                                   "Content-Length: " + std::to_string(response.length()) + "\r\n"
                                   "Connection: close\r\n"
                                   "\r\n"
                                   + response;

        send(clientSocket, fullResponse.c_str(), fullResponse.length(), 0);        
    } else {
        // Если ответ пустой, отправляем ошибку
        std::string errorResponse = "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n";
        send(clientSocket, errorResponse.c_str(), errorResponse.length(), 0);   
    }

    close(clientSocket); // Закрываем соединение
}

// processHttpRequest() Метод для обработки HTTP-запроса.
std::string HttpServer::processHttpRequest(const std::string& request) {
    std::string path = getPathFromRequest(request);
    GuestBookController controller; // Используем наследника
    return controller.handleRequest(path);
}



// getPathFromRequest() Метод для извлечения пути из HTTP-запроса.
std::string HttpServer::getPathFromRequest(const std::string& request) {
    size_t pos = request.find(' ');
    if (pos != std::string::npos) {
        size_t endPos = request.find(' ', pos + 1);
        if (endPos != std::string::npos) {
            return request.substr(pos + 1, endPos - pos - 1);
        }
    }
    return "/"; // Возвращаем корневой путь по умолчанию
}

// Запуск сервера
int main() {
    HttpServer server;
    server.start();
    return 0;
}


Компиляция:

Код: Выделить всё

g++ -std=c++11 -o server_http_class-005 server_http_class-005.cpp -lmysqlcppconn -lpthread -pthread -lssl -lcrypto
Ответить