Linux http代码

Daemon.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

const std::string nullfile = "/dev/null";

void Daemon(const std::string &cwd = "")
{
    // 1. 忽略其他异常信号
    signal(SIGCLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGSTOP, SIG_IGN);

    // 2. 将自己变成独立的会话
    if (fork() > 0)
        exit(0);
    setsid();

    // 3. 更改当前调用进程的工作目录
    if (!cwd.empty())
        chdir(cwd.c_str());

    // 4. 标准输入,标准输出,标准错误重定向至/dev/null
    int fd = open(nullfile.c_str(), O_RDWR);
    if(fd > 0)
    {
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
    }
}

HttpServer.cc

cpp 复制代码
#include "HttpServer.hpp"

#include <iostream>
#include <memory>
#include <pthread.h>

#include "Daemon.hpp"

using namespace std;

int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        exit(1);
    }
   Daemon("/home/ly_centos/http/");
    uint16_t port = std::stoi(argv[1]);
    // HttpServer *svr = new HttpServer();
    // std::unique<HttpServer> svr(new HttpServer());
    std::unique_ptr<HttpServer> svr(new HttpServer(port));
    svr->Start();
    return 0;
}

HttpServer.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <pthread.h>
#include <fstream>
#include <vector>
#include <sstream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unordered_map>


#include "Socket.hpp"


const std::string wwwroot="./wwwroot"; // web 根目录
const std::string sep = "\r\n";
const std::string homepage = "index.html";

static const int defaultport = 8082;

class HttpServer;

class ThreadData
{
public:
    ThreadData(int fd, HttpServer *s) : sockfd(fd), svr(s)
    {
    }

public:
    int sockfd;
    HttpServer *svr;
};

class HttpRequest
{
public:
    void Deserialize(std::string req)
    {
        while(true)
        {
            std::size_t pos = req.find(sep);
            if(pos == std::string::npos) break;
            std::string temp = req.substr(0, pos);
            if(temp.empty()) break;
            req_header.push_back(temp);
            req.erase(0, pos+sep.size());
        }
        text = req;
    }
    // .png:image/png
    void Parse()
    {
        std::stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;
        file_path = wwwroot; // ./wwwroot
        if(url == "/" || url == "/index.html") {
            file_path += "/";
            file_path += homepage; // ./wwwroot/index.html
        }
        else file_path += url; // /a/b/c/d.html->./wwwroot/a/b/c/d.html

        auto pos = file_path.rfind(".");
        if(pos == std::string::npos) suffix = ".html";
        else suffix = file_path.substr(pos);
    }
    void DebugPrint()
    {
        for(auto &line : req_header)
        {
            std::cout << "--------------------------------" << std::endl;
            std::cout << line << "\n\n";
        }

        std::cout << "method: " << method << std::endl;
        std::cout << "url: " << url << std::endl;
        std::cout << "http_version: " << http_version << std::endl;
        std::cout << "file_path: " << file_path << std::endl;
        std::cout << text << std::endl;
    }
public:
    std::vector<std::string> req_header;
    std::string text;

    // 解析之后的结果
    std::string method;
    std::string url;
    std::string http_version;
    std::string file_path; // ./wwwroot/a/b/c.html 2.png

    std::string suffix;
};

class HttpServer
{
public:
    HttpServer(uint16_t port = defaultport) : port_(port)
    {
        content_type.insert({".html", "text/html"});
        content_type.insert({".png", "image/png"});
    }
    bool Start()
    {
      
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
        for (;;)
        {
            std::string clientip;
            uint16_t clientport;
            int sockfd = listensock_.Accept(&clientip, &clientport);
            if (sockfd < 0)
                continue;
            perror("get a new connect, sockfd");
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd, this);
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }
    static std::string ReadHtmlContent(const std::string &htmlpath)
    {
        // 坑
        std::ifstream in(htmlpath, std::ios::binary);
        if(!in.is_open()) return "";

        in.seekg(0, std::ios_base::end);
        auto len = in.tellg();
        in.seekg(0, std::ios_base::beg);

        std::string content;
        content.resize(len);

        in.read((char*)content.c_str(), content.size());
        //std::string content;
        //std::string line;
        //while(std::getline(in, line))
        //{
        //    content += line;
        //}

        in.close();

        return content;
    }
    std::string SuffixToDesc(const std::string &suffix)
    {
        auto iter = content_type.find(suffix);
        if(iter == content_type.end()) return content_type[".html"];
        else return content_type[suffix];
    }
    void HandlerHttp(int sockfd)
    {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // bug
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer << std::endl; // 假设我们读取到的就是一个完整的,独立的http 请求
            HttpRequest req;
            req.Deserialize(buffer);
            req.Parse();
            //req.DebugPrint();

            //std::string path = wwwroot;
            //path += url; // wwwroot/a/a/b/index.html

            // 返回响应的过程
            std::string text;
            bool ok = true;
            text = ReadHtmlContent(req.file_path); // 失败?
            if(text.empty())
            {
                ok = false;
                std::string err_html = wwwroot;
                err_html += "/";
                err_html += "err.html";
                text = ReadHtmlContent(err_html);
            }

            std::string response_line;
            if(ok)
                response_line = "HTTP/1.0 200 OK\r\n";
            else
                response_line = "HTTP/1.0 404 Not Found\r\n";
            
            //response_line = "HTTP/1.0 302 Found\r\n";
            std::string response_header = "Content-Length: ";
            response_header += std::to_string(text.size()); // Content-Length: 11
            response_header += "\r\n";
            response_header += "Content-Type: ";
            response_header += SuffixToDesc(req.suffix);
            response_header += "\r\n";
            response_header += "Set-Cookie: name=haha&&passwd=12345";
            response_header += "\r\n";

            //response_header += "Location: https://www.qq.com\r\n";
            std::string blank_line = "\r\n"; // \n

            std::string response = response_line;
            response += response_header;
            response += blank_line;
            response += text;

            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }
    static void *ThreadRun(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->svr->HandlerHttp(td->sockfd);
        delete td;
        return nullptr;
    }
    ~HttpServer()
    {
    }

private:
    Sock listensock_;
    uint16_t port_;
    std::unordered_map<std::string, std::string> content_type;
};

Socket.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

enum
{
    SocketErr = 2,
    BindErr,
    ListenErr,
};

// TODO
const int backlog = 10;

class Sock
{
public:
    Sock()
    {
    }
    ~Sock()
    {
    }

public:
    void Socket()
    {
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd_ < 0)
        {
            perror("socker error");
            exit(SocketErr);
        }
        int opt = 1;
        setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    }
    void Bind(uint16_t port)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;

        if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            perror("bind error");
            exit(BindErr);
        }
    }
    void Listen()
    {
        if (listen(sockfd_, backlog) < 0)
        {
           perror("listen error");
            exit(ListenErr);
        }
    }
    int Accept(std::string *clientip, uint16_t *clientport)
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int newfd = accept(sockfd_, (struct sockaddr*)&peer, &len);
        if(newfd < 0)
        {
            perror("accept error");
            return -1;
        }
        char ipstr[64];
        inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));
        *clientip = ipstr;
        *clientport = ntohs(peer.sin_port);

        return newfd;
    }
    bool Connect(const std::string &ip, const uint16_t &port)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        peer.sin_family = AF_INET;
        peer.sin_port = htons(port);
        inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));

        int n = connect(sockfd_, (struct sockaddr*)&peer, sizeof(peer));
        if(n == -1) 
        {
            std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;
            return false;
        }
        return true;
    }
    void Close()
    {
        close(sockfd_);
    }
    int Fd()
    {
        return sockfd_;
    }

private:
    int sockfd_;
};

makefile

cpp 复制代码
HttpServer:HttpServer.cc
	g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f HttpServer

index.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>新年快乐 - 超绚烂烟花秀</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            background: #000;
            overflow: hidden;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: flex-end;
            height: 100vh;
        }
        canvas {
            position: absolute;
            top: 0;
            left: 0;
            z-index: 1;
        }
        /* 按钮样式 */
        #fireworkBtn {
            position: relative;
            z-index: 10;
            margin-bottom: 80px;
            padding: 18px 50px;
            font-size: 28px;
            font-weight: bold;
            color: #ffd700; /* 金色文字 */
            background: linear-gradient(135deg, #ff0000, #cc0000); /* 渐变红背景 */
            border: 2px solid #ffd700;
            border-radius: 12px;
            cursor: pointer;
            box-shadow: 0 0 20px rgba(255, 0, 0, 0.6), 0 0 40px rgba(255, 215, 0, 0.3);
            transition: all 0.2s;
            text-shadow: 0 0 10px rgba(255, 215, 0, 0.8);
        }
        #fireworkBtn:hover {
            transform: scale(1.08);
            box-shadow: 0 0 30px rgba(255, 0, 0, 0.8), 0 0 60px rgba(255, 215, 0, 0.5);
        }
        #fireworkBtn:active {
            transform: scale(0.96);
        }
    </style>
</head>
<body>
    <canvas id="fireworks"></canvas>
    <button id="fireworkBtn">新年快乐</button>

    <script>
        const canvas = document.getElementById('fireworks');
        const ctx = canvas.getContext('2d');
        const btn = document.getElementById('fireworkBtn');

        function resizeCanvas() {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        }
        resizeCanvas();
        window.addEventListener('resize', resizeCanvas);

        // 新年专属配色:金、银、红、青
        const colors = [
            '#ffd700', '#ffec8b', '#fff8dc', // 金色系
            '#ffffff', '#e8e8e8', '#c0c0c0', // 银色系
            '#ff0043', '#ff4500', '#ff6347', // 红色系
            '#00ffff', '#7fffd4', '#00fa9a'  // 青绿色系
        ];

        // 粒子类(带拖尾)
        class Particle {
            constructor(x, y, color, speed, angle, size) {
                this.x = x;
                this.y = y;
                this.color = color;
                this.vx = Math.cos(angle) * speed;
                this.vy = Math.sin(angle) * speed;
                this.alpha = 1;
                this.decay = Math.random() * 0.01 + 0.003;
                this.gravity = 0.03;
                this.size = size || Math.random() * 3 + 1;
                this.trail = []; // 拖尾数组
                this.maxTrailLength = 8;
            }

            update() {
                // 保存当前位置到拖尾
                this.trail.push({ x: this.x, y: this.y, alpha: this.alpha });
                if (this.trail.length > this.maxTrailLength) {
                    this.trail.shift();
                }

                this.vx *= 0.99;
                this.vy *= 0.99;
                this.vy += this.gravity;
                this.x += this.vx;
                this.y += this.vy;
                this.alpha -= this.decay;
            }

            draw() {
                // 绘制拖尾
                for (let i = 0; i < this.trail.length; i++) {
                    const t = this.trail[i];
                    const trailAlpha = (i / this.trail.length) * t.alpha * 0.5;
                    ctx.save();
                    ctx.globalAlpha = trailAlpha;
                    ctx.beginPath();
                    ctx.arc(t.x, t.y, this.size * (i / this.trail.length), 0, Math.PI * 2);
                    ctx.fillStyle = this.color;
                    ctx.fill();
                    ctx.restore();
                }

                // 绘制粒子本体(带发光效果)
                ctx.save();
                ctx.globalAlpha = this.alpha;
                ctx.shadowBlur = 10;
                ctx.shadowColor = this.color;
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
                ctx.fillStyle = this.color;
                ctx.fill();
                ctx.restore();
            }

            isAlive() {
                return this.alpha > 0;
            }
        }

        // 烟花类(双重爆炸+多种形状)
        class Firework {
            constructor() {
                this.x = Math.random() * (canvas.width * 0.6) + canvas.width * 0.2;
                this.y = canvas.height;
                this.targetY = Math.random() * (canvas.height * 0.4) + canvas.height * 0.1;
                this.color = colors[Math.floor(Math.random() * colors.length)];
                this.speed = Math.random() * 4 + 5;
                this.particles = [];
                this.exploded = false;
                this.secondExplosion = false;
                this.shape = Math.floor(Math.random() * 4); // 0:圆形 1:心形 2:星形 3:环形
            }

            update() {
                if (!this.exploded) {
                    this.y -= this.speed;
                    this.speed *= 0.99;
                    if (this.speed <= 1.5 || this.y <= this.targetY) {
                        this.explode();
                    }
                } else {
                    for (let i = this.particles.length - 1; i >= 0; i--) {
                        this.particles[i].update();
                        if (!this.particles[i].isAlive()) {
                            this.particles.splice(i, 1);
                        }
                    }

                    // 第一次爆炸后0.3秒进行第二次小爆炸
                    if (!this.secondExplosion && this.particles.length > 0 && this.particles[0].alpha < 0.7) {
                        this.secondExplode();
                    }
                }
            }

            explode() {
                this.exploded = true;
                const particleCount = 180 + Math.floor(Math.random() * 100);
                
                for (let i = 0; i < particleCount; i++) {
                    let angle, speed;
                    
                    // 根据形状计算角度和速度
                    switch (this.shape) {
                        case 0: // 圆形爆炸
                            angle = (i / particleCount) * Math.PI * 2;
                            speed = Math.random() * 4 + 2;
                            break;
                        case 1: // 心形爆炸
                            angle = (i / particleCount) * Math.PI * 2;
                            const heart = Math.pow(Math.sin(angle), 3);
                            speed = (2 - Math.cos(angle) - 2 * Math.cos(2 * angle) - Math.cos(3 * angle) - Math.cos(4 * angle)) * 1.5;
                            break;
                        case 2: // 星形爆炸
                            angle = (i / particleCount) * Math.PI * 2;
                            const star = 1 + 0.3 * Math.sin(5 * angle);
                            speed = star * (Math.random() * 3 + 2);
                            break;
                        case 3: // 环形爆炸
                            angle = (i / particleCount) * Math.PI * 2;
                            speed = 3 + Math.random() * 0.5;
                            break;
                    }

                    const size = Math.random() * 3 + 1.5;
                    const color = colors[Math.floor(Math.random() * colors.length)];
                    this.particles.push(new Particle(this.x, this.y, color, speed, angle, size));
                }
            }

            secondExplode() {
                this.secondExplosion = true;
                const secondCount = 60;
                for (let i = 0; i < secondCount; i++) {
                    const angle = Math.random() * Math.PI * 2;
                    const speed = Math.random() * 2 + 1;
                    const color = '#ffd700'; // 第二次爆炸用金色
                    this.particles.push(new Particle(this.x, this.y, color, speed, angle, 2));
                }
            }

            draw() {
                if (!this.exploded) {
                    // 绘制上升的烟花弹(带发光尾迹)
                    ctx.save();
                    ctx.shadowBlur = 15;
                    ctx.shadowColor = this.color;
                    ctx.beginPath();
                    ctx.arc(this.x, this.y, 4, 0, Math.PI * 2);
                    ctx.fillStyle = this.color;
                    ctx.fill();
                    ctx.restore();
                } else {
                    this.particles.forEach(p => p.draw());
                }
            }

            isAlive() {
                return this.exploded && this.particles.length === 0;
            }
        }

        const fireworks = [];

        function animate() {
            ctx.fillStyle = 'rgba(0, 0, 0, 0.15)';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            for (let i = fireworks.length - 1; i >= 0; i--) {
                fireworks[i].update();
                fireworks[i].draw();
                if (fireworks[i].isAlive()) {
                    fireworks.splice(i, 1);
                }
            }

            requestAnimationFrame(animate);
        }

        btn.addEventListener('click', () => {
            // 每次点击发射 5 颗烟花,超热闹
            for (let i = 0; i < 5; i++) {
                setTimeout(() => {
                    fireworks.push(new Firework());
                }, i * 120);
            }
        });

        animate();
    </script>
</body>
</html>

实际效果

相关推荐
wuxi_joe3 小时前
中国装备制造企业如何出海:以“配置管理”为核心构建全球竞争力
运维·人工智能·制造
开开心心_Every3 小时前
全屏程序切换工具,激活选中窗口快速切换
linux·运维·服务器·pdf·ocr·测试用例·模块测试
星星乘坐的船4 小时前
Centos7.9系统下docker安装
运维·docker·容器
dust_and_stars4 小时前
APT vs Snap vs Flatpak 核心对比表
运维·服务器·数据库
未来之窗软件服务4 小时前
AI人工智能(四)本地部署vosk-ASR环境命令—东方仙盟练气期
linux·运维·人工智能·本地模型·仙盟创梦ide·东方仙盟
~央千澈~4 小时前
抖音弹幕游戏开发之第17集:添加日志系统·优雅草云桧·卓伊凡
linux·服务器·前端
AIMarketing4 小时前
飞猫M505G网速技术解析峰值1.6Gbps技术原理
运维·服务器·5g
vortex55 小时前
Zellij 复制提示成功却粘贴不了?一文解决剪贴板不同步问题
linux
晚秋大魔王5 小时前
Trilium Note 服务器部署
运维·服务器