C++HTTP简易留言板

Windows

#include <iostream>
#include <fstream>
#include <string>
#include <winsock2.h>
#include <windows.h>
#include <vector>
#include <Ws2tcpip.h>
#include <sstream>
#include <iomanip>
#include <cctype>
#include <ctime>
#include <regex>

#pragma comment(lib, "ws2_32.lib")

const char* commonResponseHeaders = "Content-Type: text/html\r\n";

// URL 解码函数
#include <iostream>
#include <string>

std::string urlDecode(const std::string& str) {
    std::string result;
    for (size_t i = 0; i < str.size(); ++i) {
        if (str[i] == '%' && i + 2 < str.size() && std::isxdigit(str[i + 1]) && std::isxdigit(str[i + 2])) {
            std::string hexCode = str.substr(i + 1, 2);
            char decodedChar = static_cast<char>(std::stoi(hexCode, 0, 16));
            result += decodedChar;
            i += 2;
        }
        else if (str[i] == '+') {
            result += ' ';  // 这里应该是添加空格
        }
        else {
            result += str[i];
        }
    }
    return result;
}

// 过滤 HTML 符号的函数
std::string filterHtmlTags(const std::string& message) {
    std::regex htmlTagsRegex("<[^>]*>");
    return std::regex_replace(message, htmlTagsRegex, "");
}

void handleRequest(SOCKET clientSocket) {
    char buffer[1024];
    int bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);  // 确保不会超出缓冲区
    if (bytesRead <= 0) {
        std::cerr << "Error receiving data from client" << std::endl;
        return;
    }
    buffer[bytesRead] = '\0';  // 确保字符串以 null 结尾

    std::string request(buffer);

    // 获取当前日期并构建文件路径
    time_t t = time(NULL);
    std::tm localTime;
    localtime_s(&localTime, &t);
    std::string folderName = "rec_request";
    CreateDirectoryA(folderName.c_str(), NULL);
    std::string fileName = folderName + "/" + std::to_string(1900 + localTime.tm_year) + "-" + std::to_string(1 + localTime.tm_mon) + "-" + std::to_string(localTime.tm_mday) + ".log";

    // 获取当前时间并格式化
    char timeBuffer[26];
    ctime_s(timeBuffer, sizeof(timeBuffer), &t);
    std::string timeStr(timeBuffer);
    timeStr.pop_back();  // 去除换行符

    // 打开或创建文件并写入时间和请求
    std::ofstream file(fileName, std::ios::app);
    file << timeStr << std::endl;
    file << std::string(64, '=') << std::endl;
    file << request << std::endl;
    file.close();

    std::cout << "Received request:\n" << request << std::endl;

    std::string method, uri, version;
    size_t firstSpace = request.find(' ');
    size_t secondSpace = request.find(' ', firstSpace + 1);

    if (firstSpace != std::string::npos && secondSpace != std::string::npos) {
        method = request.substr(0, firstSpace);
        uri = request.substr(firstSpace + 1, secondSpace - firstSpace - 1);
        version = request.substr(secondSpace + 1, request.find("\r\n") - secondSpace - 1);
    }
    else {
        std::cerr << "Invalid request" << std::endl;
        return;
    }

    std::cout << "Method: " << method << ", URI: " << uri << ", Version: " << version << std::endl;

    // 处理根路径和 /message_board
    if (uri == "/" || uri == "/message_board") {
        std::string response = "HTTP/1.1 200 OK\r\n";
        response += commonResponseHeaders;
        response += "\r\n";
        response += "<html><style>";
        response += "body { font-family: Arial, sans-serif; background-color: #f0f0f0; }";
        response += "h2 { color: #333; }";
        response += ".container { max-width: 800px; margin: 0 auto; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); }";
        response += ".message-form { margin-bottom: 20px; }";
        response += ".message { border: 1px solid #ccc; padding: 10px; margin: 10px 0; background-color: #fff; word-wrap: break-word; }";
        response += "input[type='text'] { width: 100%; height: 200px; }";  // 设置输入框高度为 200 像素
        response += "input[type='submit'] { width: 100%; padding: 10px; font-size: 16px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }";
        response += "input[type='submit']:hover { background-color: #45a049; }";
        response += "</style></head><body>";
        response += "<div class='container'>";
        response += "<h2>留言板</h2>";
        response += "<div class='message-form'><form action='/submit_message' method='post'>";
        response += "留言: <input type='text' name='message' required><br>";
        response += "<input type='submit' value='提交'>";
        response += "</form></div>";

        // 读取并显示留言内容
        std::ifstream logFile("message.txt");  // 更改文件名
        if (logFile.is_open()) {
            std::string line;
            while (std::getline(logFile, line)) {
                response += "<div class='message'>" + line + "</div>";
            }
            logFile.close();
        }

        response += "<div style='font-size:12px; color:#999; text-align:center;'>Power by Hank Q:1101773014</div>";  // 添加版权信息

        response += "</div></body></html>";

        send(clientSocket, response.c_str(), response.size(), 0);
    }
    else if (uri == "/submit_message" && method == "POST") {
        size_t contentLengthStart = request.find("Content-Length: ");
        if (contentLengthStart == std::string::npos) {
            std::cerr << "Invalid submit message request" << std::endl;
            return;
        }

        size_t contentLengthEnd = request.find("\r\n", contentLengthStart);
        size_t contentLength = std::stoi(request.substr(contentLengthStart + 16, contentLengthEnd - contentLengthStart - 16));

        size_t bodyStart = request.find("\r\n\r\n") + 4;
        std::string body = request.substr(bodyStart, contentLength);
        size_t messageStart = body.find("message=") + 8;
        std::string submittedMessage = urlDecode(body.substr(messageStart));

        // 过滤 HTML 符号
        submittedMessage = filterHtmlTags(submittedMessage);

        // 获取当前时间
        std::time_t t = std::time(nullptr);
        std::tm localTime;
        localtime_s(&localTime, &t);
        std::ostringstream oss;
        oss << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S");

        SOCKADDR_IN clientAddress;
        int clientAddressLength = sizeof(clientAddress);
        if (getpeername(clientSocket, (SOCKADDR*)&clientAddress, &clientAddressLength) == SOCKET_ERROR) {
            std::cerr << "Error getting client IP address" << std::endl;
            return;
        }

        char ipAddressBuffer[INET_ADDRSTRLEN];
        if (inet_ntop(AF_INET, &clientAddress.sin_addr, ipAddressBuffer, INET_ADDRSTRLEN) == NULL) {
            std::cerr << "Error converting IP address" << std::endl;
            return;
        }
        std::string clientIP = std::string(ipAddressBuffer);

        std::ofstream logFile("message.txt", std::ios::app);  // 更改文件名
        logFile << oss.str() << " " << clientIP << ": " << submittedMessage << std::endl;
        logFile.close();

        // 重定向回主页
        std::string response = "HTTP/1.1 303 See Other\r\n";
        response += "Location: /\r\n";
        response += "\r\n";

        send(clientSocket, response.c_str(), response.size(), 0);
    }
    else {
        std::string response = "HTTP/1.1 404 Not Found\r\n";
        response += commonResponseHeaders;
        response += "\r\n";
        response += "<html><body>";
        response += "<h2>404 Not Found</h2>";
        response += "</body></html>";

        send(clientSocket, response.c_str(), response.size(), 0);
    }

    closesocket(clientSocket);
}

int main() {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        std::cerr << "WSAStartup failed" << std::endl;
        return 1;
    }

    SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket == INVALID_SOCKET) {
        std::cerr << "Error creating socket" << std::endl;
        WSACleanup();
        return 1;
    }

    SOCKADDR_IN serverAddress;
    memset(&serverAddress, 0, sizeof(serverAddress));
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = INADDR_ANY;
    serverAddress.sin_port = htons(80);

    if (bind(serverSocket, (SOCKADDR*)&serverAddress, sizeof(serverAddress)) == SOCKET_ERROR) {
        std::cerr << "Error binding socket" << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

    if (listen(serverSocket, 5) == SOCKET_ERROR) {
        std::cerr << "Error listening on socket" << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 1;
    }

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

    while (true) {
        SOCKADDR_IN clientAddress;
        int clientAddressLength = sizeof(clientAddress);
        SOCKET clientSocket = accept(serverSocket, (SOCKADDR*)&clientAddress, &clientAddressLength);
        if (clientSocket == INVALID_SOCKET) {
            std::cerr << "Error accepting client connection" << std::endl;
            continue;
        }

        handleRequest(clientSocket);
    }

    closesocket(serverSocket);
    WSACleanup();

    return 0;
}


Linux

#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <vector>
#include <sstream>
#include <iomanip>
#include <cctype>
#include <ctime>
#include <regex>
#include <codecvt>

// URL 解码函数
std::string urlDecode(const std::string& str) {
    std::string result;
    for (size_t i = 0; i < str.size(); ++i) {
        if (str[i] == '%' && i + 2 < str.size() && std::isxdigit(str[i + 1]) && std::isxdigit(str[i + 2])) {
            std::string hexCode = str.substr(i + 1, 2);
            char decodedChar = static_cast<char>(std::stoi(hexCode, 0, 16));
            result += decodedChar;
            i += 2;
        }
        else if (str[i] == '+') {
            result += ' ';
        }
        else {
            result += str[i];
        }
    }
    return result;
}

// 过滤 HTML 符号的函数
std::string filterHtmlTags(const std::string& message) {
    std::regex htmlTagsRegex("<[^>]*>");
    return std::regex_replace(message, htmlTagsRegex, "");
}

void handleRequest(int clientSocket) {
    char buffer[1024];
    int bytesRead = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);  // 确保不会超出缓冲区
    if (bytesRead <= 0) {
        std::cerr << "Error receiving data from client" << std::endl;
        return;
    }
    buffer[bytesRead] = '\0';  // 确保字符串以 null 结尾

    std::string request(buffer);

    // 获取当前日期并构建文件路径
    time_t t = time(NULL);
    struct tm localTime;
    localtime_r(&t, &localTime);
    std::string folderName = "rec_request";
    mkdir(folderName.c_str(), 0777);
    std::string fileName = folderName + "/" + std::to_string(1900 + localTime.tm_year) + "-" + std::to_string(1 + localTime.tm_mon) + "-" + std::to_string(localTime.tm_mday) + ".log";

    // 获取当前时间并格式化
    char timeBuffer[26];
    ctime_r(&t, timeBuffer);
    std::string timeStr(timeBuffer);
    timeStr.pop_back();  // 去除换行符

    // 打开或创建文件并写入时间和请求
    std::ofstream file(fileName, std::ios::app);
    file << timeStr << std::endl;
    file << std::string(64, '=') << std::endl;

    struct sockaddr_in clientAddress;
    socklen_t clientAddressLength = sizeof(clientAddress);
    if (getpeername(clientSocket, (struct sockaddr*)&clientAddress, &clientAddressLength) == -1) {
        std::cerr << "Error getting client IP address" << std::endl;
        return;
    }

    char ipAddressBuffer[INET_ADDRSTRLEN];
    if (inet_ntop(AF_INET, &clientAddress.sin_addr, ipAddressBuffer, INET_ADDRSTRLEN) == NULL) {
        std::cerr << "Error converting IP address" << std::endl;
        return;
    }
    std::string clientIP = std::string(ipAddressBuffer);

    file << "Client IP: " << clientIP << std::endl;
    file << "Request Headers:\n" << request << std::endl;  // 写入请求头

    std::cout << "Received request:\n" << request << std::endl;

    std::string method, uri, version;
    size_t firstSpace = request.find(' ');
    size_t secondSpace = request.find(' ', firstSpace + 1);

    if (firstSpace != std::string::npos && secondSpace != std::string::npos) {
        method = request.substr(0, firstSpace);
        uri = request.substr(firstSpace + 1, secondSpace - firstSpace - 1);
        version = request.substr(secondSpace + 1, request.find("\r\n") - secondSpace - 1);
    }
    else {
        std::cerr << "Invalid request" << std::endl;
        return;
    }

    std::cout << "Method: " << method << ", URI: " << uri << ", Version: " << version << std::endl;

    // 处理根路径和 /message_board
    if (uri == "/" || uri == "/message_board") {
        std::string response = "HTTP/1.1 200 OK\r\n";
        response += "Content-Type: text/html; charset=utf-8\r\n";
        response += "\r\n";
        response += "<html><style>";
        response += "body { font-family: Arial, sans-serif; background-color: #f0f0f0; }";
        response += "h2 { color: #333; }";
        response += ".container { max-width: 800px; margin: 0 auto; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); }";
        response += ".message-form { margin-bottom: 20px; }";
        response += ".message { border: 1px solid #ccc; padding: 10px; margin: 10px 0; background-color: #fff; word-wrap: break-word; }";
        response += "input[type='text'] { width: 100%; height: 200px; }";
        response += "input[type='submit'] { width: 100%; padding: 10px; font-size: 16px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }";
        response += "input[type='submit']:hover { background-color: #45a049; }";
        response += "</style></head><body>";
        response += "<div class='container'>";
        response += "<h2>留言板</h2>";
        response += "<div class='message-form'><form action='/submit_message' method='post'>";
        response += "留言: <input type='text' name='message' required><br>";
        response += "<input type='submit' value='提交'>";
        response += "</form></div>";

        // 读取并显示留言内容
        std::ifstream logFile("message.txt");
        if (logFile.is_open()) {
            std::string line;
            while (std::getline(logFile, line)) {
                response += "<div class='message'>" + line + "</div>";
            }
            logFile.close();
        }

        response += "<div style='font-size:12px; color:#999; text-align:center;'>Power by Hank Q:1101773014</div>";
        response += "</div></body></html>";

        send(clientSocket, response.c_str(), response.size(), 0);
    }
    else if (uri == "/submit_message" && method == "POST") {
        size_t contentLengthStart = request.find("Content-Length: ");
        if (contentLengthStart == std::string::npos) {
            std::cerr << "Invalid submit message request" << std::endl;
            return;
        }

        size_t contentLengthEnd = request.find("\r\n", contentLengthStart);
        size_t contentLength = std::stoi(request.substr(contentLengthStart + 16, contentLengthEnd - contentLengthStart - 16));

        size_t bodyStart = request.find("\r\n\r\n") + 4;
        std::string body = request.substr(bodyStart, contentLength);
        size_t messageStart = body.find("message=") + 8;
        std::string submittedMessage = urlDecode(body.substr(messageStart));

        // 过滤 HTML 符号
        submittedMessage = filterHtmlTags(submittedMessage);

        // 获取当前时间
        std::time_t t = std::time(nullptr);
        struct tm localTime;
        localtime_r(&t, &localTime);
        std::ostringstream oss;
        oss << std::put_time(&localTime, "%Y-%m-%d %H:%M:%S");

        // 在写入文件的地方进行修改
        std::ofstream logFile("message.txt", std::ios::app);
        std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;  // 创建编码转换器

        // 假设要写入的字符串是 submittedMessage
        std::wstring wideMessage = conv.from_bytes(submittedMessage);  // 转换为宽字符字符串
        logFile << oss.str() << " " << clientIP << ": " << conv.to_bytes(wideMessage) << std::endl;  // 以 utf-8 编码写入
        logFile.close();

        // 重定向回主页
        std::string response = "HTTP/1.1 303 See Other\r\n";
        response += "Location: /\r\n";
        response += "\r\n";

        send(clientSocket, response.c_str(), response.size(), 0);
    }
    else {
        std::string response = "HTTP/1.1 404 Not Found\r\n";
        response += "Content-Type: text/html\r\n";
        response += "\r\n";
        response += "<html><body>";
        response += "<h2>404 Not Found</h2>";
        response += "</body></html>";

        send(clientSocket, response.c_str(), response.size(), 0);
    }

    close(clientSocket);
}

int main() {
    int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket == -1) {
        std::cerr << "Error creating socket" << std::endl;
        return 1;
    }

    struct sockaddr_in serverAddress;
    memset(&serverAddress, 0, sizeof(serverAddress));
    serverAddress.sin_family = AF_INET;
    serverAddress.sin_addr.s_addr = INADDR_ANY;
    serverAddress.sin_port = htons(80);

    if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {
        std::cerr << "Error binding socket" << std::endl;
        close(serverSocket);
        return 1;
    }

    if (listen(serverSocket, 5) == -1) {
        std::cerr << "Error listening on socket" << std::endl;
        close(serverSocket);
        return 1;
    }

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

    while (true) {
        struct sockaddr_in clientAddress;
        socklen_t clientAddressLength = sizeof(clientAddress);
        int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressLength);
        if (clientSocket == -1) {
            std::cerr << "Error accepting client connection" << std::endl;
            continue;
        }

        handleRequest(clientSocket);
    }

    close(serverSocket);

    return 0;
}

Linux开机启动配置文件

# /etc/systemd/system vi http-server.service
[Unit]
Description=My HTTP Server
After=network.target

[Service]
ExecStart=/root/projects/留言板Linux/bin/x64/Release/留言板Linux.out
WorkingDirectory=/root/projects/留言板Linux/bin/x64/Release
User=root
Group=root
Restart=always

[Install]
WantedBy=multi-user.target

启动代码

sudo systemctl daemon-reload
sudo systemctl enable http-server.service
sudo systemctl start http-server.service
systemctl status http-server.service # 看到 active (running) 就成功了

在这里插入图片描述
http://1.92.93.88/

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值