使用C++实现HTTP服务

发布于:2025-03-31 ⋅ 阅读:(18) ⋅ 点赞:(0)

天天开心!!!
阅读本篇文章之前,请先阅读HTTP基础知识

传送门----> HTTP基础知识


一、C++Web服务器(核心代码WebServer.cpp)

要实现一个简单的C++Web服务器,支持GET和POST请求,同时能够提供静态文件(如JavaScript、CSS和图片文件)。

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <unordered_map>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/stat.h>
using namespace std;

#define PORT 8080   // 端口号

#define BUFFER_SIZE 4096   // 缓冲区大小

//根据文件拓展名返回适当的Content-Type
std::string getContentType(const std::string &path){
    if(path.ends_with(".html")) return "text/html";
    if(path.ends_with(".css")) return "text/css";
    if(path.ends_with(".js")) return "application/javascript";
    if(path.ends_with(".png")) return "image/png";
    if(path.ends_with(".jpg")) return "image/jpeg";
    if(path.ends_with(".jpeg")) return "image/jpeg";
    if(path.ends_with(".gif")) return "image/gif";
    return "text/plain";

}

//读取静态文件
std::string readFile(const std::string &path){
    //创建一个输入文件流对象,以二进制模式打开指定路径的文件
    std::ifstream file(path,std::ios::binary);

    //检查文件是否成功打开
    if(!file.is_open()){
        perror("file open failed");
        //如果打开失败返回空字符串
        return "";
    }
    //创建一个输出字符串流对象,用于将文件内容读取到字符串
    std::ostringstream ss;

    //使用输出字符串流对象的rdbuf()将文件内容读取到字符串中
    //这实际上是将文件流中的所有字节复制到字符串流的缓冲区中
    ss<<file.rdbuf();

    //将字符串流的内容转换为std::string并返回
    return ss.str();

}

//判断文件是否存在
bool fileExists(const std::string &path){
    struct stat buffer;
    return stat(path.c_str(), &buffer)==0;
}

int main() {
    // 创建套接字
    int server_fd, new_socket;
    // 地址结构体
    struct sockaddr_in address;

    int opt=1;  // 设置套接字选项
    int addlen = sizeof(address);// 地址结构体长度

    char buffer[BUFFER_SIZE] = {0};   // 缓冲区

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

    //设置端口复用
    //如果你不是用SO_REUSEADDR,服务器程序关闭后,可能会出现“Address already in use” 错误
    //尤其是当你尝试在同一个端口上重新启动服务器时,这是因为TCP连接可能会保持TIME_WAIT的状态一段时间
    //而SO_REUSEADDR允许套接字在这种状态下重新绑定到另一个端口
    if(setsoocketopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))){
        perror("setsoocketopt failed");
        exit(EXIT_FAILURE);
    }

    //配置服务器地址
    addrss.sin_family = AF_INET;
    addrss.sin_addr.s_addr = INADDR_ANY;
    addrss.sin_port = htons(PORT);

    //绑定套接字
    if(bind(server_fd, (struct sockaddr *)&address, addlen)<0){
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    //监听请求
    if(listen(server_fd, 3)<0){
        perror("listen failed");
        exit(EXIT_FAILURE);
    }
    cout<<"WebServer is running on port"<<PORT<<"....\n";

    while(true)
    {
        //接受客户端连接
        if((new_socket=accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addlen))<0){
            perror("accept failed");
            exit(EXIT_FAILURE);
        }

        //读取客户端请求
        read(new_socket, buffer, BUFFER_SIZE);
        cout<<"Request received :\n"<<buffer<<endl;

        //解析Http请求
        //这段代码是解析Http请求中提取 请求方法、路径和Http版本。如下:
        //第一次提取method,得到:GET
        //第二次提取path,得到:/index.html
        //第三次提取version,得到:HTTP/1.1
        std::istringstream request(buffer);
        std::string method , path , version;
        request>>method>>path>>version;

        //默认首页
        if(path=="/")
        {
            path="/index.html";
        }
        //构建文件路径
        std::string filePath ="."+path;

        //处理GET请求
        if(method=="GET"){
            if(fileExists(filePath)){//文件存在
                std::string content = readFile(filePath);//读取文件内容
                std::string contentType = getContentType(filePath);//获取文件类型

                std::ostringstream response;  // 构建响应
                response<<"HTTP/1.1 200 OK\r\n"
                        <<"Content-Type:"<<contentType<<"\r\n"
                        <<"Content-Length:"<<content.length()<<"\r\n"
                        <<"\r\n"
                        <<content;//响应头和响应体
                response<<content;

                //发送响应
                send(new_socket, response.str().c_str(), response.str().length(), 0);
            }else{
                //404 NOT FOUND
                std::string notFound="HTTP/1.1 404 Not Found\r\n\r\n404 Not Found";
                send(new_socket, notFound.c_str(), notFound.length(), 0);
            }
        }
        else if(method=="POST"){//处理POST请求
            //简单的响应客户端的POST请求(可以根据需要解析POST请求)
            std::string response="HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nPOST request";
            send(new_socket, response.c_str(), response.length(), 0);
        }else{
            //405 METHOD NOT ALLOWED
            std::string notAllowed="HTTP/1.1 405 Method Not Allowed\r\n\r\n405 Method Not Allowed";
            send(new_socket, notAllowed.c_str(), notAllowed.length(), 0);
        }
        //关闭连接
        close(new_socket);

    }
    //释放资源,关闭服务器套接字
    close(server_fd);

    return 0;
}

二、静态文件结构

  • 确保你的HTML、CSS、JS和图片文件都存放在与可执行程序相同的目录下或适当的子目录中,例如:
./index.html
./style.css
./script.js
./images/example.jjpg

在这里插入图片描述

假设HTL文件是这样的:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>这是一个简单的WebServer</title>
    <style>
        body{
            font-family: Arial,sans-serif;
            background-color: #f4f4f9;
            color: #333;
            margin: 0;
            padding: 0;
        }
        header{
            background-color: #4CAF50;
            color: white;
            padding: 10px 0;
        }
        h1{
            margin: 0;
        }
        p{
            font-size: 1.2em;
            margin: 20px auto;
            width: 80%;
        }
        footer{
            position: fixed;
            bottom: 0;
            width: 100%;
            background-color: #4CAF50;
            color: white;
            padding: 10px 0;
        }
    </style>
</head>
<body>
<header>
    <h1>这是一个简单的WebServer</h1>
</header>
<main>
    <p>这是一个简单的WebServer,你可以在这里放置一些内容。</p>
    <br/>
    <img src="23.png" alt="C++测试">
</main>
<footer>
    <p>
        &copy;2025 My WebServer By HuJiaHang
    </p>
</footer>

</body>
</html>

三、编译和运行

  • 编译
g++ -std=c++20 WebServer.cpp -o WebServer
//使用C++20标准,是因为std::string::ends_with()是C++20引入的功能
  • 运行:执行编译好的WebServer程序
./WebServer

四、访问测试

  • 使用浏览器访问http://localhost:8080,可以获取静态HTML页面、CSS样式、JavaScript脚本和图片
  • 注意:如果使用的是云服务器,就必须要在安全组中放开端口
  • 以下是我的测试
    在这里插入图片描述

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到