【基于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;
}

【基于C++HTTP 服务器的epoll 改造】,c++,http,服务器
【基于C++HTTP 服务器的epoll 改造】,c++,http,服务器
后续加上post 请求

原文链接: link文章来源地址https://www.toymoban.com/news/detail-785692.html

到了这里,关于【基于C++HTTP 服务器的epoll 改造】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于C++的简单HTTP服务器实现

    基于C++的简单HTTP服务器实现 一个Web Server就是一个服务器软件(程序),或者是运行这个服务器软件的硬件(计算机)。其主要功能是通过HTTP协议与客户端(通常是浏览器(Browser))进行通信,来接收,存储,处理来自客户端的HTTP请求,并对其请求做出HTTP响应,返回给客户

    2024年02月02日
    浏览(49)
  • 基于 Python 实现一个简单的 HTTP 服务器

    文章这个题目,让我想起了大学时上《Unix 网络编程》这门专业课的家庭作业,题目几乎一模一样。 HTTP 服务器工作在服务端,主要功能包括处理来自客户端的请求,管理网络资源,以及生成和发送响应给客户端。在实际应用中,HTTP 服务器不仅限于传输 HTML 文档;它还可以传

    2024年03月22日
    浏览(58)
  • 基于epoll实现Reactor服务器

    在我们调用epoll_create的时候会创建出epoll模型,这个模型也是利用文件描述类似文件系统的方式控制该结构。 在我们调用epoll_create的时候,就会在内核管理中创建一个epoll模型,并且建管理模块地址给file结构体,file结构体也是连接在管理所有file结构体的数据结构中 所以epo

    2024年02月20日
    浏览(42)
  • Java服务器调用Python服务器进行交互:基于Http协议的Restful风格调用(Springboot/FastApi)

    实现Java服务器调用Python服务器进行交互以及数据传输,可采用以下方法,亲测有效: 基于Restful风格进行请求调用: 框架 : Java服务器采用Springboot框架进行搭建服务 python服务器采用FastApi框架进行搭建服务 思路 :前端–Java–python–Java–前端 代码设计 :Axios-@PostMapping-请求

    2024年04月26日
    浏览(43)
  • 基于 ESP32 创建 HTTP Server 服务器,支持载入文件到服务器,并对载入文件进行删除管理

    软件编程指南参见:HTTP 服务器 任意一款 ESP32 系列开发板 2.4GHz 路由器热点 准备各种格式的文件 可基于 esp-idf/examples/protocols/http_server /file_serving 例程进行测试 只需要设置 ESP32 连接的 2.4GHz 的 WiFi 热点 即可 menuconfig — Example Connection Configuration — WiFi SSID — WiFi Password 下载固件

    2024年02月16日
    浏览(54)
  • 【Qt-4】QT基于qhttp-server搭建http服务器

    写在前面 :虽然qhttp-server编译成功,已生成dll库,但在使用过程中,仍出现无法打开文件: “QtSslServer/QtSslServer”的问题,在多次解决无果后,决定放弃qhttp-server,选择了QWebAPP,望知道的大佬可以帮忙解答一下,感谢~~ 一、环境搭建 1、下载及解压源文件 下载QtHttpServer源码,

    2024年02月08日
    浏览(55)
  • 基于Spring Boot2.0 & HTTP/2 实现服务器、客户端

    HTTP协议由于其无状态的特性以及超高的普及率,是当下大部分网站选择使用的应用层协议。然而,HTTP/1.x的底层传输方式的几个特性,已经对应用的整体性能产生了负面影响。特别是,HTTP/1.0在每次的TCP连接上只允许发送一次请求,在HTTP/1.1中增加了请求管线,但是这仅仅解决

    2023年04月09日
    浏览(63)
  • 基于epoll的TCP服务器端(C++)

    网络编程——C++实现socket通信(TCP)高并发之epoll模式_tcp通信c++ 多客户端epoll_n大橘为重n的博客-CSDN博客 网络编程——C++实现socket通信(TCP)高并发之select模式_n大橘为重n的博客-CSDN博客 server.cpp   client.cpp

    2024年02月12日
    浏览(43)
  • Android-音视频学习系列-(八)基于-Nginx-搭建(rtmp、http)直播服务器

    #!/bin/sh HTTP_FLV_MODULE_PATH=…/nginx-http-flv-module-1.2.7 OpenSSL_PATH=…/openssl-1.1.1d #–prefix=./bin 代表编译完成之后输出的路径地址 #–add-module 将拓展模块添加到当前一起编译 ./configure --prefix=./bin –add-module= H T T P F L V M O D U L E P A T H   − − w i t h − o p e n s s l = HTTP_FLV_MODULE_PATH --with

    2024年04月15日
    浏览(61)
  • 基于多反应堆的高并发服务器【C/C++/Reactor】(中)HttpRequest模块 解析http请求协议

    一、HTTP响应报文格式 二、根据解析出的原始数据,对客户端的请求做出处理  processHttpRequest  1.解码字符串   解决浏览器无法访问带特殊字符的文件得到问题 2.判断文件扩展名,返回对应的 Content-Type(Mime-Type) 3.发送文件  sendFile 4.发送目录 三、解析http请求协议  parseHttpR

    2024年02月02日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包