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>

实际效果

相关推荐
林姜泽樾3 小时前
Linux入门第十二章,创建用户、用户组、主组附加组等相关知识详解
linux·运维·服务器·centos
xiaokangzhe3 小时前
Linux系统安全
linux·运维·系统安全
feng一样的男子3 小时前
NFS 扩展属性 (xattr) 提示操作不支持解决方案
linux·go
xiaokangzhe3 小时前
Nginx核心功能
运维·nginx
松果1773 小时前
以本地时钟为源的时间服务器
运维·chrony·时间服务器
Highcharts.js4 小时前
Highcharts React v4.2.1 正式发布:更自然的React开发体验,更清晰的数据处理
linux·运维·javascript·ubuntu·react.js·数据可视化·highcharts
ayaya_mana4 小时前
快速安装Nginx-UI:让Nginx管理可视化的高效方案
运维·nginx·ui
c++之路5 小时前
Linux网络协议与编程基础:TCP/IP协议族全解析
linux·网络协议·tcp/ip
Charlie__ZS5 小时前
Ubuntu 22.04新建用户,并赋予管理权限
linux·os·ubuntn
Johnstons6 小时前
读懂 TCP 标志位:网络运维中的“信号灯”
运维·网络·tcp/ip