【基于C++HTTP 服务器的epoll 改造】

简介: 【基于C++HTTP 服务器的epoll 改造】

打印模块

Log.hpp 方便使用

#pragma once
#include <iostream>
#include <string>
#include <ctime>
#define INFO    1
#define WARNING 2
#define ERROR   3
#define FATAL   4
#define LOG(level, message) Log(#level, message, __FILE__, __LINE__)
void Log(std::string level, std::string message, std::string file_name, int line)
{
    std::cout<<"["<<level<<"]["<<time(nullptr)<<"]["<<message<<"]["<<file_name<<"]["<<line<<"]"<<std::endl;
}

TcpServer.hpp

#pragma once
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Log.hpp"
#define BACKLOG 5
class TcpServer{
    private:
        int _port; //端口号
        int _listen_sock; //监听套接字
        static TcpServer* _svr;
        int epollfd;
    private:
        TcpServer(int port)
            :_port(port)
            ,_listen_sock(-1)
        {}
        TcpServer(const TcpServer&)
        {}
    public:
        static TcpServer* GetInstance(int port)
        {
            static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
            if(_svr == nullptr){
                pthread_mutex_lock(&lock);
                if(_svr == nullptr){
                    _svr = new TcpServer(port);
                    _svr->InitServer();
                }
                pthread_mutex_unlock(&lock);
            }
            return _svr;
        }
        void InitServer()
        {
            Socket();
            Bind();
            Listen();
            LOG(INFO, "tcp_server init ... success");
        }
        void Socket()
        {
            _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
            if(_listen_sock < 0){
                LOG(FATAL, "socket error!");
                exit(1);
            }
            int opt = 1;
            setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
            LOG(INFO, "create socket ... success");
        }
        void Bind()
        {
            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; //云服务器不能直接绑定公网IP
            if(bind(_listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
                LOG(FATAL, "bind error!");
                exit(2);
            }
            LOG(INFO, "bind socket ... success");
        }
        void Listen()
        {
            if(listen(_listen_sock, BACKLOG) < 0){
                LOG(FATAL, "listen error!");
                exit(3);
            }
            LOG(INFO, "listen socket ... success");
        }
        int Sock()
        {
            return _listen_sock;
        }
        ~TcpServer()
        {
            if(_listen_sock >= 0){
                close(_listen_sock);
            }
        }
};
TcpServer* TcpServer::_svr = nullptr;

HttpServer.hpp

#pragma once
#include <iostream>
#include <sys/epoll.h>
#include <signal.h>
#include "TcpServer.hpp"
#include "Task.hpp"
#include "ThreadPool.hpp"
#include "Log.hpp"
#define PORT 8081
#define MAX_EVENT_NUMBER 1024
class HttpServer{
    private:
        int _port;
        bool _stop;
        int epollfd;
        struct epoll_event events[MAX_EVENT_NUMBER];
    public:
        HttpServer(int port)
            :_port(port)
        {
            _stop = false;
            epollfd = -1;
        }
        void InitServer()
        {
            signal(SIGPIPE, SIG_IGN); //忽略SIGPIPE信号,防止写入时崩溃
        }
    int setnonblocking(int fd)
    {
      int option = fcntl(fd, F_GETFL) |  O_NONBLOCK;
      fcntl(fd, F_SETFD, option);
      return fcntl(fd, F_GETFL);
    }
    void addfd(int fd, bool enable_et){
      struct epoll_event ev;
      ev.data.fd = fd;
      ev.events = EPOLLIN;
      if(enable_et){
        ev.events |= EPOLLET;
      }
      epoll_ctl(this->epollfd, EPOLL_CTL_ADD, fd, &ev);
      setnonblocking(fd);
    }
        void work(int nread, int listenfd)
        {
              for(int i = 0; i < nread; i++)
              {
                int sockfd = events[i].data.fd;
                if(sockfd == listenfd){
                    struct sockaddr_in peer;
                    memset(&peer, 0, sizeof(peer));
                    socklen_t len = sizeof(peer);
                    int connfd = accept(listenfd, (struct sockaddr*)&peer, &len);
                    addfd(connfd, true);
                }else if(events[i].events &  EPOLLIN){
                    LOG(INFO, "event trigger once");
                    LOG(INFO, "get a new link");
                    Task task(sockfd);
                    ThreadPool::GetInstance()->PushTask(task);   
                }else{
                    LOG(INFO, "something else happened");
                }
              }  
        }
        void Loop()
        {
            LOG(INFO, "loop begin");
            TcpServer* tsvr = TcpServer::GetInstance(_port);
            int listen_sock = tsvr->Sock();
            epollfd = epoll_create(5);
            addfd(listen_sock, true);
            while(!_stop){
                int nread = epoll_wait(this->epollfd, events, MAX_EVENT_NUMBER, -1);
                if(nread < 0){
                    continue;
                }
                // struct sockaddr_in peer;
                // memset(&peer, 0, sizeof(peer));
                // socklen_t len = sizeof(peer);
                // int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
                // if(sock < 0){
                //     continue;
                // }
                // LOG(INFO, "get a new link");
                // Task task(sock);
                // ThreadPool::GetInstance()->PushTask(task);
                //lt(nread, listen_sock);
                work(nread, listen_sock);
                //int* p = new int(sock);
                //pthread_t tid;
                //pthread_create(&tid, nullptr, Entrance::HandlerRequest, (void*)p);
                //pthread_detach(tid);
            }
        }
        ~HttpServer()
        {}
};

Task.hpp

#pragma once
#include <iostream>
#include <unistd.h>
#include "Protocol.hpp"
class Task{
    private:
        int _sock;
        CallBack _handler; //回调
    public:
        Task()
        {}
        Task(int sock)
            :_sock(sock)
        {}
        //处理任务
        void ProcessOn()
        {
            _handler(_sock); //调用CallBack的仿函数
        }
        ~Task()
        {}
};

ThreadPool.hpp

#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include "Task.hpp"
#include "Log.hpp"
#define NUM 6
class ThreadPool{
    private:
        std::queue<Task> _task_queue; //任务队列
        int _num;
        bool _stop;
        pthread_mutex_t _mutex;
        pthread_cond_t _cond;
        static ThreadPool* _inst;
    private:
        //构造函数私有
        ThreadPool(int num = NUM)
            :_num(num)
            ,_stop(false)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }
        //拷贝构造函数私有或删除
        ThreadPool(const ThreadPool&)=delete;
        bool IsEmpty()
        {
            return _task_queue.empty();
        }
        bool IsStop()
        {
            return _stop;
        }
        void LockQueue()
        {
            pthread_mutex_lock(&_mutex);
        }
        void UnLockQueue()
        {
            pthread_mutex_unlock(&_mutex);
        }
        void ThreadWait()
        {
            pthread_cond_wait(&_cond, &_mutex);
        }
        void ThreadWakeUp()
        {
            pthread_cond_signal(&_cond);
        }
    public:
        static ThreadPool* GetInstance()
        {
            static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
            if(_inst == nullptr){
                pthread_mutex_lock(&mtx);
                if(_inst == nullptr){
                    _inst = new ThreadPool();
                    _inst->InitThreadPool();
                }
                pthread_mutex_unlock(&mtx);
            }
            return _inst;
        }
        static void* ThreadRoutine(void* arg)
        {
            pthread_detach(pthread_self());
            ThreadPool* tp = (ThreadPool*)arg;
            while(true){
                tp->LockQueue();
                while(tp->IsEmpty()){
                    tp->ThreadWait();
                }
                Task task;
                tp->PopTask(task);
                tp->UnLockQueue();
                task.ProcessOn(); //处理任务
            }
        }
        bool InitThreadPool()
        {
            pthread_t tid;
            for(int i = 0;i < _num;i++){
                if(pthread_create(&tid, nullptr, ThreadRoutine, this) != 0){
                    LOG(FATAL, "create thread pool error!");
                    return false;
                }
            }
            LOG(INFO, "create thread pool success!");
            return true;
        }
        void PushTask(const Task& task)
        {
            LockQueue();
            _task_queue.push(task);
            UnLockQueue();
            ThreadWakeUp();
        }
        void PopTask(Task& task)
        {
            task = _task_queue.front();
            _task_queue.pop();
        }
        ~ThreadPool()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_cond);
        }
};
ThreadPool* ThreadPool::_inst = nullptr;

Util.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
//工具类
class Util{
    public:
        static int ReadLine(int sock, std::string& out)
        {
            char ch = 'X'; //随便设置一个字符,只要不是\n即可
            while(ch != '\n'){
                ssize_t size = recv(sock, &ch, 1, 0);
                if(size > 0){
                    if(ch == '\r'){
                        //窥探
                        recv(sock, &ch, 1, MSG_PEEK);
                        if(ch == '\n'){
                            //窥探成功
                            //\r\n->\n
                            recv(sock, &ch, 1, 0);
                        }
                        else{
                            //\r->\n
                            ch = '\n';
                        }
                    }
                    //普通字符或\n
                    out.push_back(ch);
                }
                else if(size == 0){
                    return 0;
                }
                else{
                    return -1;
                }
            }
            return out.size();
        }
        static bool CutString(std::string& target, std::string& sub1_out, std::string& sub2_out, std::string sep)
        {
            size_t pos = target.find(sep, 0);
            if(pos != std::string::npos){
                sub1_out = target.substr(0, pos);
                sub2_out = target.substr(pos + sep.size());
                return true;
            }
            return false;
        }
};

Protocol.hpp

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <algorithm>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <sys/wait.h>
#include <fcntl.h>
#include "Util.hpp"
#include "Log.hpp"
#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"
#define PAGE_400 "400.html"
#define PAGE_404 "404.html"
#define PAGE_500 "500.html"
#define OK 200
#define BAD_REQUEST 400
#define NOT_FOUND 404
#define SERVER_ERROR 500
static std::string CodeToDesc(int code)
{
    std::string desc;
    switch(code){
        case 200:
            desc = "OK";
            break;
        case 404:
            desc = "Not Found";
            break;
        default:
            break;
    }
    return desc;
}
static std::string SuffixToDesc(const std::string& suffix)
{
    static std::unordered_map<std::string, std::string> suffix_to_desc = {
        {".html", "text/html"},
        {".css", "text/css"},
        {".js", "application/x-javascript"},
        {".jpg", "application/x-jpg"},
        {".xml", "text/xml"}
    };
    auto iter = suffix_to_desc.find(suffix);
    if(iter != suffix_to_desc.end()){
        return iter->second;
    }
    return "text/html";
}
class HttpRequest{
    public:
        std::string _request_line; //请求行
        std::vector<std::string> _request_header; //请求报头
        std::string _blank; //空行
        std::string _request_body; //请求正文
        //解析完毕之后的结果
        std::string _method; //请求方法
        std::string _uri; //URI
        std::string _version; //版本号
        std::unordered_map<std::string, std::string> _header_kv; //请求报头中的键值对
        int _content_length; //正文长度
        std::string _path; //请求资源的路径
        std::string _query_string; //uri中携带的参数
        bool _cgi; //是否需要使用CGI模式
    public:
        HttpRequest()
            :_content_length(0)
            ,_cgi(false)
        {}
        ~HttpRequest()
        {}
};
class HttpResponse{
    public:
        std::string _status_line; //状态行
        std::vector<std::string> _response_header; //响应报头
        std::string _blank; //空行
        std::string _response_body; //响应正文
        int _status_code; //状态码
        int _fd; //响应的文件
        int _size; //响应文件的大小
        std::string _suffix; //响应文件的后缀
    public:
        HttpResponse()
            :_blank(LINE_END)
            ,_status_code(OK)
            ,_fd(-1)
            ,_size(0)
        {}
        ~HttpResponse()
        {}
};
//读取请求、分析请求、构建响应
//IO通信
class EndPoint{
    private:
        int _sock;
        HttpRequest _http_request;
        HttpResponse _http_response;
        bool _stop;
    private:
        //读取请求行
        bool RecvHttpRequestLine()
        {
            auto& line = _http_request._request_line;
            if(Util::ReadLine(_sock, line) > 0){
                line.resize(line.size() - 1);
                LOG(INFO, line);
            }
            else{
                _stop = true; //读取出错,不予处理
            }
            return _stop;
        }
        //读取请求报头和空行
        bool RecvHttpRequestHeader()
        {
            std::string line;
            while(true){
                line.clear(); //每次读取之前清空line
                if(Util::ReadLine(_sock, line) <= 0){
                    _stop = true;
                    break;
                }
                if(line == "\n"){
                    _http_request._blank = line;
                    break;
                }
                line.resize(line.size() - 1);
                _http_request._request_header.push_back(line);
                //LOG(INFO, line);
            }
            return _stop;
        }
        //解析请求行
        void ParseHttpRequestLine()
        {
            auto& line = _http_request._request_line;
            std::stringstream ss(line);
            ss>>_http_request._method>>_http_request._uri>>_http_request._version;
            auto& method = _http_request._method;
            std::transform(method.begin(), method.end(), method.begin(), toupper);
        }
        //解析请求报头
        void ParseHttpRequestHeader()
        {
            std::string key;
            std::string value;
            for(auto& iter : _http_request._request_header){
                if(Util::CutString(iter, key, value, SEP))
                {
                    _http_request._header_kv.insert({key, value});
                }
            }
        }
        //判定是否需要读取请求正文
        bool IsNeedRecvHttpRequestBody()
        {
            auto& method = _http_request._method;
            if(method == "POST"){
                auto& header_kv = _http_request._header_kv;
                auto iter = header_kv.find("Content-Length");
                if(iter != header_kv.end()){
                    _http_request._content_length = atoi(iter->second.c_str());
                    return true;
                }
            }
            return false;
        }
        //读取请求正文
        bool RecvHttpRequestBody()
        {
            if(IsNeedRecvHttpRequestBody()){
                int content_length = _http_request._content_length;
                auto& body = _http_request._request_body;
                char ch = 0;
                while(content_length){
                    ssize_t size = recv(_sock, &ch, 1, 0);
                    if(size > 0){
                        body.push_back(ch);
                        content_length--;
                    }
                    else{
                        _stop = true;
                        break;
                    }
                }
            }
            return _stop;
        }
        //CGI处理
        int ProcessCgi()
        {
            int code = OK;
            auto& bin = _http_request._path; //要让子进程执行的目标程序
            auto& method = _http_request._method;
            //父进程的数据
            auto& query_string = _http_request._query_string; //GET
            auto& request_body = _http_request._request_body; //POST
            int content_length = _http_request._content_length;
            auto& response_body = _http_response._response_body;
            //站在父进程角度
            int input[2];
            int output[2];
            if(pipe(input) < 0){
                LOG(ERROR, "pipe input error!");
                code = SERVER_ERROR;
                return code;
            }
            if(pipe(output) < 0){
                LOG(ERROR, "pipe output error!");
                code = SERVER_ERROR;
                return code;
            }
            pid_t pid = fork();
            if(pid == 0){ //child
                close(input[0]);
                close(output[1]);
                //将请求方法通过环境变量传参
                std::string method_env = "METHOD=";
                method_env += method;
                putenv((char*)method_env.c_str());
                if(method == "GET"){ //通过环境变量传参
                    std::string query_env = "QUERY_STRING=";
                    query_env += query_string;
                    putenv((char*)query_env.c_str());
                    LOG(INFO, "GET Method, Add Query_String env");
                }
                else if(method == "POST"){ //导入正文参数长度
                    std::string content_length_env = "CONTENT_LENGTH=";
                    content_length_env += std::to_string(content_length);
                    putenv((char*)content_length_env.c_str());
                    LOG(INFO, "POST Method, Add Content_Length env");
                }
                else{
                    //Do Nothing
                }
                dup2(output[0], 0);
                dup2(input[1], 1);
                execl(bin.c_str(), bin.c_str(), nullptr);
                exit(1);
            }
            else if(pid < 0){
                LOG(ERROR, "fork error!");
                code = SERVER_ERROR;
                return code;
            }
            else{ //father
                close(input[1]);
                close(output[0]);
                if(method == "POST"){ //将数据写入到管道当中
                    const char* start = request_body.c_str();
                    int total = 0;
                    int size = 0;
                    while(total < content_length && (size = write(output[1], start + total, request_body.size() - total)) > 0){
                        total += size;
                    }
                }
                //std::string test_string = "2021dragon";
                //send(output[1], test_string.c_str(), test_string.size(), 0);
                char ch = 0;
                while(read(input[0], &ch, 1) > 0){
                    response_body.push_back(ch);
                } //不会一直读,当另一端关闭后会继续执行下面的代码
                int status = 0;
                pid_t ret = waitpid(pid, &status, 0);
                if(ret == pid){
                    if(WIFEXITED(status)){ //正常退出
                        LOG(INFO, "正常退出");
                        if(WEXITSTATUS(status) == 0){ //结果正确
                            LOG(INFO, "正常退出,结果正确");
                            code = OK;
                        }
                        else{
                            LOG(INFO, "正常退出,结果不正确");
                            code = BAD_REQUEST;
                        }
                    }
                    else{
                        LOG(INFO, "异常退出");
                        code = SERVER_ERROR;
                    }
                }
                //释放文件描述符
                close(input[0]);
                close(output[1]);
            }
            return code;
        }
        //非CGI处理
        int ProcessNonCgi()
        {
            //打开待发送的文件
            _http_response._fd = open(_http_request._path.c_str(), O_RDONLY);
            if(_http_response._fd >= 0){ //打开文件成功再构建
                return OK;
            }
            return NOT_FOUND;
        }
        void BuildOkResponse()
        {
            //构建响应报头
            std::string content_type = "Content-Type: ";
            content_type += SuffixToDesc(_http_response._suffix);
            content_type += LINE_END;
            _http_response._response_header.push_back(content_type);
            std::string content_length = "Content-Length: ";
            if(_http_request._cgi){ //以CGI方式请求
                content_length += std::to_string(_http_response._response_body.size());
            }
            else{ //以非CGI方式请求
                content_length += std::to_string(_http_response._size);
            }
            content_length += LINE_END;
            _http_response._response_header.push_back(content_length);
        }
        void HandlerError(std::string page)
        {
            _http_request._cgi = false; //正常的网页返回,非CGI返回
            _http_response._fd = open(page.c_str(), O_RDONLY);
            std::cout<<page.c_str()<<std::endl;
            if(_http_response._fd > 0){
                std::cout<<page.c_str()<<std::endl;
                //构建响应报头
                struct stat st;
                stat(page.c_str(), &st);
                std::string content_type = "Content-Type: text/html";
                content_type += LINE_END;
                _http_response._response_header.push_back(content_type);
                std::string content_length = "Content-Length: ";
                content_length += std::to_string(st.st_size);
                content_length += LINE_END;
                _http_response._response_header.push_back(content_length);
                _http_response._size = st.st_size;
            }
        }
        void BuildHttpResponseHelp()
        {
            int code = _http_response._status_code;
            //构建状态行
            auto& status_line = _http_response._status_line;
            status_line += HTTP_VERSION;
            status_line += " ";
            status_line += std::to_string(code);
            status_line += " ";
            status_line += CodeToDesc(code);
            status_line += LINE_END;
            //构建响应正文,可能包括响应报头
            std::string path = WEB_ROOT;
            path += "/";
            switch(code){
                case OK:
                    BuildOkResponse();
                    break;
                case NOT_FOUND:
                    path += PAGE_404;
                    HandlerError(path);
                    break;
                case BAD_REQUEST:
                    path += PAGE_400;
                    HandlerError(path);
                    break;
                case SERVER_ERROR:
                    path += PAGE_500;
                    HandlerError(path);
                    break;
                default:
                    break;
            }
        }
    public:
        EndPoint(int sock)
            :_sock(sock)
            ,_stop(false)
        {}
        bool IsStop()
        {
            return _stop;
        }
        //读取请求
        void RecvHttpRequest()
        {
            if(!RecvHttpRequestLine()&&!RecvHttpRequestHeader()){ //短路求值
                ParseHttpRequestLine();
                ParseHttpRequestHeader();
                RecvHttpRequestBody();
            }
        }
        //构建响应
        void BuildHttpResponse()
        {
            auto& code = _http_response._status_code;
            std::string path;
            struct stat st;
            size_t pos = 0;
            if(_http_request._method != "GET"&&_http_request._method != "POST"){
                //非法请求
                LOG(WARNING, "method is not right");
                code = BAD_REQUEST;
                goto END;
            }
            if(_http_request._method == "GET"){
                size_t pos = _http_request._uri.find('?');
                if(pos != std::string::npos){
                    Util::CutString(_http_request._uri, _http_request._path, _http_request._query_string, "?");
                    _http_request._cgi = true; //上传了参数,需要使用CGI模式
                }
                else{
                    _http_request._path = _http_request._uri;
                }
            }
            else if(_http_request._method == "POST"){
                _http_request._path = _http_request._uri;
                _http_request._cgi = true; //上传了参数,需要使用CGI模式
            }
            else{
                //Do Nothing
            }
            path = _http_request._path;
            _http_request._path = WEB_ROOT;
            _http_request._path += path;
            if(_http_request._path[_http_request._path.size() - 1] == '/'){
                _http_request._path += HOME_PAGE;
            }
            std::cout<<"debug: "<<_http_request._path.c_str()<<std::endl;
            if(stat(_http_request._path.c_str(), &st) == 0){
                //资源存在
                if(S_ISDIR(st.st_mode)){ //是一个目录,并且不会以/结尾,因为前面已经处理过了
                    _http_request._path += "/";
                    _http_request._path += HOME_PAGE;
                    stat(_http_request._path.c_str(), &st); //path改变,需要重新获取属性
                }
                else if(st.st_mode&S_IXUSR||st.st_mode&S_IXGRP||st.st_mode&S_IXOTH){ //是一个可执行程序,需要特殊处理
                    _http_request._cgi = true; //请求的是一个可执行程序,需要使用CGI模式
                }
                _http_response._size = st.st_size;
            }
            else{
                //资源不存在
                LOG(WARNING, _http_request._path + " NOT_FOUND");
                code = NOT_FOUND;
                goto END;
            }
            pos = _http_request._path.rfind('.');
            if(pos == std::string::npos){
                _http_response._suffix = ".html"; //默认设置
            }
            else{
                _http_response._suffix = _http_request._path.substr(pos);
            }
            if(_http_request._cgi == true){
                code = ProcessCgi(); //以CGI的方式进行处理
            }
            else{
                code = ProcessNonCgi(); //简单的网页返回,返回静态网页
            }
END:
            BuildHttpResponseHelp();
        }
        //发送响应
        void SendHttpResponse()
        {
            //发送状态行
            send(_sock, _http_response._status_line.c_str(), _http_response._status_line.size(), 0);
            //发送响应报头
            for(auto& iter : _http_response._response_header){
                send(_sock, iter.c_str(), iter.size(), 0);
            }
            //发送空行
            send(_sock, _http_response._blank.c_str(), _http_response._blank.size(), 0);
            //发送响应正文
            if(_http_request._cgi){
                auto& response_body = _http_response._response_body;
                const char* start = response_body.c_str();
                size_t size = 0;
                size_t total = 0;
                while(total < response_body.size()&&(size = send(_sock, start + total, response_body.size() - total, 0)) > 0){
                    total += size;
                }
            }
            else{
                sendfile(_sock, _http_response._fd, nullptr, _http_response._size);
                //关闭文件
                close(_http_response._fd);
            }
        }
        ~EndPoint()
        {}
};
class CallBack{
    public:
        CallBack()
        {}
        void operator()(int sock)
        {
            HandlerRequest(sock);
        }
        void HandlerRequest(int sock)
        {
            LOG(INFO, "handler request begin");
            std::cout<<"get a new link..."<<sock<<std::endl;
#ifdef DEBUG
            char buffer[4096];
            recv(sock, buffer, sizeof(buffer), 0);
            std::cout<<"------------------begin------------------"<<std::endl;
            std::cout<<buffer<<std::endl;
            std::cout<<"-------------------end-------------------"<<std::endl;
#else
            EndPoint* ep = new EndPoint(sock);
            ep->RecvHttpRequest();
            if(!ep->IsStop()){
                LOG(INFO, "Recv No Error, Begin Build And Send");
                ep->BuildHttpResponse();
                ep->SendHttpResponse();
            }
            else{
                LOG(WARNING, "Recv Error, Stop Build And Send");
            }
            close(sock);
            delete ep;
#endif
            LOG(INFO, "handler request end");
        }
        ~CallBack()
        {}
};

Makefile

bin=httpserver
cgi=test_cgi
cc=g++
LD_FLAGS=-std=c++11 -lpthread #-DDEBUG=1
curr=$(shell pwd)
src=main.cc
ALL:$(bin) $(cgi)
.PHONY:ALL
$(bin):$(src)
  $(cc) -o $@ $^ $(LD_FLAGS)
$(cgi):cgi/test_cgi.cc
  $(cc) -o $@ $^ -std=c++11
.PHONY:clean
clean:
  rm -f $(bin) $(cgi)
  rm -rf output
.PHONY:output
output:
  mkdir -p output
  cp $(bin) output
  cp -rf wwwroot output
  cp $(cgi) output/wwwroot
  cp ./cgi/shell_cgi.sh output/wwwroot
  cp ./cgi/python_cgi.py output/wwwroot
  cp ./cgi/test.html output/wwwroot

main.cc

#include <iostream>
#include <string>
#include <memory>
#include "HttpServer.hpp"
static void Usage(std::string proc)
{
    std::cout<<"Usage:\n\t"<<proc<<" port"<<std::endl;
}
int main(int argc, char* argv[])
{
    if(argc != 2){
        Usage(argv[0]);
        exit(4);
    }
    int port = atoi(argv[1]);
    std::shared_ptr<HttpServer> svr(new HttpServer(port));
    svr->InitServer();
    svr->Loop();
    return 0;
}

后续加上post 请求

原文链接: link

相关文章
|
20天前
|
搜索推荐 安全 网络安全
服务器支持HTTPS的时机和条件
【10月更文挑战第23天】服务器支持HTTPS的时机和条件
15 5
|
1月前
使用Netty实现文件传输的HTTP服务器和客户端
本文通过详细的代码示例,展示了如何使用Netty框架实现一个文件传输的HTTP服务器和客户端,包括服务端的文件处理和客户端的文件请求与接收。
39 1
使用Netty实现文件传输的HTTP服务器和客户端
|
14天前
|
存储 Oracle 关系型数据库
oracle服务器存储过程中调用http
通过配置权限、创建和调用存储过程,您可以在Oracle数据库中使用UTL_HTTP包发起HTTP请求。这使得Oracle存储过程可以与外部HTTP服务进行交互,从而实现更复杂的数据处理和集成。在实际应用中,根据具体需求调整请求类型和错误处理逻辑,以确保系统的稳定性和可靠性。
16 0
|
1月前
|
Linux C++
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
24 0
Linux C/C++之IO多路复用(poll,epoll)
|
1月前
|
Linux C语言 C++
vsCode远程执行c和c++代码并操控linux服务器完整教程
这篇文章提供了一个完整的教程,介绍如何在Visual Studio Code中配置和使用插件来远程执行C和C++代码,并操控Linux服务器,包括安装VSCode、安装插件、配置插件、配置编译工具、升级glibc和编写代码进行调试的步骤。
206 0
vsCode远程执行c和c++代码并操控linux服务器完整教程
|
1月前
|
Linux 开发工具 C语言
【c++】c++发送http请求
【c++】c++发送http请求
|
1月前
|
存储 监控 NoSQL
Redis的实现二: c、c++的网络通信编程技术,让服务器处理多个client
本文讨论了在C/C++中实现服务器处理多个客户端的技术,重点介绍了事件循环和非阻塞IO的概念,以及如何在Linux上使用epoll来高效地监控和管理多个文件描述符。
27 0
|
1月前
|
数据采集 Web App开发 数据安全/隐私保护
User-Agent在C++ HTTP请求中的作用
User-Agent在C++ HTTP请求中的作用
|
2月前
|
开发者
HTTP状态码是由网页服务器返回的三位数字响应代码,用于表示请求的处理结果和状态
HTTP状态码是由网页服务器返回的三位数字响应代码,用于表示请求的处理结果和状态
32 1
|
7天前
|
机器学习/深度学习 人工智能 弹性计算
什么是阿里云GPU云服务器?GPU服务器优势、使用和租赁费用整理
阿里云GPU云服务器提供强大的GPU算力,适用于深度学习、科学计算、图形可视化和视频处理等多种场景。作为亚太领先的云服务提供商,阿里云的GPU云服务器具备灵活的资源配置、高安全性和易用性,支持多种计费模式,帮助企业高效应对计算密集型任务。

热门文章

最新文章